From: Dave Chinner <dgc@kernel.org>
To: MingTao Huang <1037827920@qq.com>
Cc: Carlos Maiolino <cem@kernel.org>,
Dave Chinner <dchinner@redhat.com>,
"Darrick J . Wong" <djwong@kernel.org>,
Chandan Babu R <chandanbabu@kernel.org>,
linux-xfs@vger.kernel.org, linux-kernel@vger.kernel.org,
MingTao Huang <mintaohuang@tencent.com>
Subject: Re: [PATCH] xfs: fix skipped flushing items not counted in xfsaild_push()
Date: Wed, 23 Sep 2026 08:12:46 +1000 [thread overview]
Message-ID: <arL9Xrmf5Q7wd3T4@dread> (raw)
In-Reply-To: <tencent_F801A9436DFB447AAFEC63D3E615B8052405@qq.com>
On Tue, Sep 22, 2026 at 04:14:29PM +0800, MingTao Huang wrote:
> From: MingTao Huang <mintaohuang@tencent.com>
>
> Commit f3f7ae68a4ea ("xfs: skip flushing log items during push")
> introduced a fast path in xfsaild_push() that uses
> test_bit(XFS_LI_FLUSHING) to skip log items already being
> flushed. However, the fast path jumps directly to the
> next_item label, bypassing flushing++, count++, and the
> ail_last_pushed_lsn update. This causes three problems:
Right, they get skipped because they are already flushing and we are
not doing anything with them. i.e. they've already been accounted as
flushing (e.g. inodes that were gathered into a single buffer flush
by xfs_iflush_cluster()) and the buffer is already either on the
delwri list for submission or under IO.
>
> 1. The loop exit condition "count > 1000" becomes much harder to
> trigger. count was meant to track every item visited, but now it
No it isn't.
As the author of this code, I can say for certain that the intent of
'count' is to count the number of items we made pushing decisions
about, not count the number of items we have iterated.
The purpose of 'flushing' is to account for the number of buffers we
accumulate on the delwri list before we submit it. As count is
incremented whenever flushing is incremented, it currently forms
an upper bound to the number of buffers that can be queued on the
delwri list in a single push iteration.
The purpose of 'stuck' is to account for items that could not be
placed on the delwri list because they were locked, pinned or
otherwise unavailable for flushing.
'success' is not directly counted, because there is no further
action that needs to be taken for them; they are simply counted,
and hence contribute to the flush/stuck ratio decisions later.
IOWs, every 'push decision' that could add a buffer to the delwri
list is accounted by 'count', and flushing/stuck indicate what
decision was made.
The point of LI_FLUSHING was simply to skip over all the items that
would not change either count, flushing or stuck because they have
already been accounted to 'count' and 'flushing' by a prior push
decision.
> only increments for non-flushing items that enter
> xfsaild_push_item().
Exactly - count is the number of items we made new pushing decsions
about.
> Each such item typically triggers an inode
> cluster flush that marks dozens of neighbouring inodes as flushing,
> so count effectively counts cluster flushes rather than individual
> items.
Yes, that is exactly the intent - a single cluster buffer on the
delwri list covers up to 32 inode items on the AIL. That is -one IO-
for up to 32 items on the AIL, and it is -IO- that we are trying to
account for and optimise here, not log items.
> The threshold shifts from 1000 items to ~1000 clusters,
No, the count has always been '1000 new buffer IO decisions made',
not '1000 items processed'.
> letting the loop scan an order of magnitude more items per round.
> Each cluster flush adds a buffer to ail_buf_list, and the resulting
> oversized list causes xfs_buf_delwri_submit_nowait() -- which runs
> list_sort() plus per-buffer trylock and IO submission -- to take so
> long that the watchdog fires.
How? count bounds the delwri list to 1000 buffers at most. That's
very intentional, and the number is based on benchmarking results.
i.e. A thousand items on a list takes a couple of milliseconds to
sort. I/O submission of that list takes a couple of milliseconds if
it doesn't block (i.e. the IO subsystem has to be faster than the
CPU IO submission loop to avoid block on a full queue), and if it
does block, we want all the IO submission to merge as effectively as
possible in the queue so that we use as few IO slots in the queue as
possible.
Hence the submission code should not be running around in a hard
loop for 22s because of a list that is too long. The buffer list
length is -bounded- by count, and that should prevent these sorts of
issues.
> 2. The timeout decision "(stuck + flushing) * 100 / count > 90" is
> computed without the fast-path flushing items, so the flushing ratio
> is severely under-reported.
No, the accounting this check is acting on is correct. The action we
take is based on the all the items we tried to push this iteration,
not the indirect LI_FLUSHING items that we didn't make a decision
about or are still in progress from the previous iteration.
> When most of the AIL is flushing, the
> ratio appears near 0%.
How did you get into a state where most of the AIL is flushing and
no progress is being made transitioning items out of flushing state?
i.e. if delwri submission is not making progress, then the ail push
will keep appending to the delwri list (by design, so we retry
flushing items until submission succeeds). This will result in
transitioning most of the AIL to flushing state and queued on the
delwri list. This is a symptom of the delwri submission not
making progress and rather than a problem with AIL flushing
accounting.
i.e. we need to know why delwri submission is not making progress,
as that is likely the real cause of the problem. i.e. it is unlikely
that this has anythign to do with how we account for item push
decisions, and it is likely your changes just slow down or change
the timing of item pushing sufficiently to avoid whatever issue is
occuring in delwri submission.
> xfsaild therefore selects
> "tout = 0" when it should select "tout = 20" (20 ms
> back-off to let IO complete). The zero-backoff tight loop compounds
> the ail_buf_list accumulation across rounds.
>
> 3. ail_last_pushed_lsn is not advanced past flushing items, so the
> next push round restarts scanning from the same position, repeatedly
> traversing items that are still in-flight.
This is why we do not account for flushing items that we skip. It will
walk over them as fast as possible until it reaches items that it
can push. Those items will then be accounted as
flushing/stuck/count. This will then move the last_lsn forwards
appropriately or trigger a log force or backoff sleep and so it
should not get stuck spinning for 22s in that case, either.
> We hit this as a soft lockup during stress testing on an internal
> kernel that includes commit f3f7ae68a4ea ("xfs: skip flushing log
> items during push"). The xfsaild kthread was stuck for
> 22 seconds inside xfs_buf_delwri_submit_nowait(), called from
> xfsaild_push(), processing an excessively large ail_buf_list:
Tell me how the delwri list got so long that it gets stuck inside
xfs_buf_delwri_submit_nowait(). Whatever caused that is the bug that
we need to understand and fix - changing accounting to make gross
behavioural changes that result in exceedingly inefficient CPU usage
and break IO optimisations is not the way to address a list length
that has apparently excceeded the bounds built into the code...
-Dave.
--
Dave Chinner
dgc@kernel.org
next prev parent reply other threads:[~2026-09-22 22:13 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-22 8:14 MingTao Huang
2026-09-22 15:02 ` Darrick J. Wong
2026-09-22 22:12 ` Dave Chinner [this message]
2026-09-23 13:44 ` Brian Foster
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=arL9Xrmf5Q7wd3T4@dread \
--to=dgc@kernel.org \
--cc=1037827920@qq.com \
--cc=cem@kernel.org \
--cc=chandanbabu@kernel.org \
--cc=dchinner@redhat.com \
--cc=djwong@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
--cc=mintaohuang@tencent.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®