From: Zhang Yi <yizhang089@gmail.com>
To: Jan Kara <jack@suse.cz>
Cc: Zhang Yi <yi.zhang@huaweicloud.com>,
linux-ext4@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org, tytso@mit.edu,
adilger.kernel@dilger.ca, libaokun@linux.alibaba.com,
ojaswin@linux.ibm.com, ritesh.list@gmail.com, djwong@kernel.org,
hch@infradead.org, yi.zhang@huawei.com, yangerkun@huawei.com,
yukuai@fnnas.com
Subject: Re: [PATCH v4 18/23] ext4: wait for ordered I/O in the iomap buffered I/O path
Date: Tue, 30 Jun 2026 20:06:55 +0800 [thread overview]
Message-ID: <8999aa70-073c-47f9-ae6e-39af3cc1fe32@gmail.com> (raw)
In-Reply-To: <3n6ge2tfe4bd65eicc7l2csxkzupcjtdtc4ojpl6bvtf7cwll6@sxmlt3uciw4c>
On 6/25/2026 5:42 PM, Jan Kara wrote:
> On Mon 22-06-26 11:32:16, Zhang Yi wrote:
>> On 6/18/2026 9:48 PM, Jan Kara wrote:
>>> On Mon 11-05-26 15:23:38, Zhang Yi wrote:
>>>> From: Zhang Yi <yi.zhang@huawei.com>
>>>>
>>>> For append writes, wait for ordered I/O to complete before updating
>>>> i_disksize. This ensures that zeroed data is flushed to disk before the
>>>> metadata update, preventing stale data from being exposed during
>>>> unaligned post-EOF append writes.
>>>>
>>>> Suggested-by: Jan Kara <jack@suse.cz>
>>>> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
>>>
>>> Frankly, this all looks too complex to me. Plus your are adding 32-bytes to
>>> struct ext4_inode_info which isn't great either. Why don't you just do
>>> filemap_fdatawait() for the byte at old i_disksize and be done with it?
>>>
>>> I believe we have to simplify this. All this complexity (and thus
>>> maintenance burden) across several patches for the corner case of zeroing
>>> tail block on extention is in my opinion difficult to justify.
>>>
>>> Honza
>>
>> Hi, Jan!
>>
>> Thanks for the review. I understand the concern about complexity and the
>> 32-byte increase to ext4_inode_info. I tried using
>> filemap_fdatawait_range() as you suggested, but found two issues where
>> this solution doesn't work.
>>
>> 1. ioend worker deadlock
>>
>> Since worker concurrency resources are limited, we cannot wait for
>> another ioend worker to complete within one ioend worker with the same
>> work_struct. If the worker calls
>> filemap_fdatawait_range(byte_at_old_disksize) to wait for the zeroed
>> block's folio writeback to complete, it sleeps holding the only worker
>> slot. If the folio contains blocks requiring extent conversion, its
>> writeback bit is cleared by iomap_finish_ioends() running inside
>> another worker -- which can only run after the current worker finishes
>> its batch.
>>
>> Concretely:
>> - Worker W1 processes ioend A, calls filemap_fdatawait_range() on
>> the old EOF byte, sleeps.
>> - The zeroed data is in ioend B. bio_endio defers it to
>> i_iomap_ioend_list and calls queue_work().
>> - queue_work() on i_iomap_ioend_work is idempotent: it returns false
>> because the work is currently executing (even though sleeping).
>> - ioend B sits in the list, never gets processed.
>> - The folio writeback bit is only cleared by processing ioend B.
>> - W1 sleeps forever -> deadlock.
>
> Yes, good point. We cannot wait for folio writeback completion from end_io
> processing for another folio.
>
>> Therefore, I think we have to put the wakeup logic in
>> ext4_iomap_end_bio() that runs in interrupt context without consuming
>> a worker thread. The ordered range tracking and wait queue are what
>> make that possible.
>>
>> 2. Truncate-up needs an accurate state query
>>
>> In the follow-up patch 19, ext4_set_inode_size() must make a precise
>> decision when updating i_disksize during truncate up.
>>
>> This needs a state query: "is there ordered zero I/O in flight right
>> now?" If yes, the i_disksize update is deferred to
>> ext4_iomap_wb_update_disksize(is_ordered=true), which advances
>> i_disksize to i_size when the ordered I/O completes. If no, we must
>> advance i_disksize immediately, otherwise we will lose the updating
>> forever.
>>
>> Therefore, we need to track the state of the ordered range. Simply
>> using filemap_fdatawait_range() doesn't work. i_ordered_len serves as
>> a maintained state flag that both the ioend completion path and the
>> setattr path can read atomically without sleeping.
>>
>> Suggestions?
>
> I see. Thanks for explanation. I went back to our discussion back from
> February to remind myself about the constraints on the tail block zeroing
> and the i_disksize update mechanism. And in the light of complexity of the
> current mechanism, I think we've discarded the following possibility too
> easily:
>
> * On file extend / truncate up just zero tail folio in the page cache, mark
> it dirty, keep i_disksize at old value, update i_size to the new value,
> add inode to orphan list.
> If the i_disksize was block aligned (and so we skip zeroing), we just
> update i_disksize rightaway.
>
> * In io end processing if the folio for which we end io has a block which
> straddles i_disksize, we update i_disksize to current i_size. We defer
> removing inode from orphan list e.g. to file close time (doing it from
> end_io processing is problematic locking wise as we need i_rwsem for it).
>
> This is a very simple scheme with very good performace. It makes sure stale
> data in the tail block cannot be seen on disk after a crash.
Hmm, I think this solution has a problem. Since i_disksize is advanced
to i_size only when the ioend covering the i_disksize block completes,
we must ensure that i_size is updated before the zeroed folio's
writeback completes. Otherwise the ioend worker reads the old i_size,
advances i_disksize to the old value (a no-op), and i_disksize stays
stale forever after i_size is updated.
Currently, neither truncate-up nor file-extend paths guarantee this
ordering -- in both, ext4_block_zero_eof() runs before the i_size
update.
For truncate-up and fallocate, we might be able to split
ext4_update_inode_size() / ext4_set_inode_size() so that i_size is
updated before ext4_block_zero_eof(), and then decide whether to
update i_disksize based on whether i_disksize is block-aligned. I'm
not yet sure what side effects this reordering would have, but I feel
uneasy about this.
The append-write case is hard to solve. We can't know the final i_size
before the write completes, so we can't update i_size ahead of
ext4_block_zero_eof(). When the zeroed folio is later written back, it's
treated as an ordinary overwriting write -- i_disksize is not advanced.
And when the subsequent buffered-write data is written back, i_disksize
still isn't advanced either. The size update is lost.
>
> Potential problems with this:
>
> * We need to do i_disksize update from io end processing which means starting a
> transaction. So this mechanism has to be restricted to buffered IO iomap
> path due to locking constraints. If the inode uses old ordered mode, it
> has to keep using it also for handling of the tail block zeroing.
> I think that's acceptable as we want to transition everything to iomap
> anyway so this duplicity will eventually go away.
>
> * After a crash we can see i_disksize already updated but file content will
> show zeros. This is not breaking any guarantees, it just changes how ext4
> behaves after a crash. Again, I think this is acceptable tradeoff for the
> simplicity.
These two points are fine with me. My current implementation also has
these same tradeoffs.
>
> What do you think? Any problems I have missed? I'm sorry for poking into
> this mechanism again and again but I want to keep the code as simple as
> possible to make our life easier in the future...
That's fine. Although it's a corner case, it's quite subtle, and
handling it properly takes some careful thought. I would very much
like to solve it in a simple and elegant way. Any good suggestions
are welcome.
Thanks,
Yi.
>
>> Regarding the bloat of ext4_inode_info, perhaps we can drop the
>> wait_queue_head_t (24 bytes) and use wait_var_event()/ wake_up_var()
>> instead. Would this be acceptable?
>
> Yes, that would be a good way to reduce the bloat if we still need this.
>
> Honza
next prev parent reply other threads:[~2026-06-30 12:07 UTC|newest]
Thread overview: 105+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-11 7:23 [PATCH v4 00/23] ext4: use iomap for regular file's buffered I/O path Zhang Yi
2026-05-11 7:23 ` [PATCH v4 01/23] ext4: simplify size updating in ext4_setattr() Zhang Yi
2026-05-19 5:24 ` Ojaswin Mujoo
2026-05-11 7:23 ` [PATCH v4 02/23] ext4: factor out ext4_truncate_[up|down]() Zhang Yi
2026-05-19 6:05 ` Ojaswin Mujoo
2026-06-16 9:31 ` Jan Kara
2026-05-11 7:23 ` [PATCH v4 03/23] ext4: simplify error handling in ext4_setattr() Zhang Yi
2026-05-19 6:08 ` Ojaswin Mujoo
2026-06-16 9:36 ` Jan Kara
2026-05-11 7:23 ` [PATCH v4 04/23] ext4: add iomap address space operations for buffered I/O Zhang Yi
2026-05-19 9:28 ` Ojaswin Mujoo
2026-05-19 12:35 ` Zhang Yi
2026-05-19 16:53 ` Ojaswin Mujoo
2026-05-20 2:49 ` Zhang Yi
2026-05-26 17:11 ` Ojaswin Mujoo
2026-05-11 7:23 ` [PATCH v4 05/23] ext4: implement buffered read path using iomap Zhang Yi
2026-05-19 10:00 ` Ojaswin Mujoo
2026-05-11 7:23 ` [PATCH v4 06/23] ext4: pass out extent seq counter when mapping da blocks Zhang Yi
2026-05-19 10:02 ` Ojaswin Mujoo
2026-06-16 10:04 ` Jan Kara
2026-06-16 12:37 ` Zhang Yi
2026-06-17 20:29 ` Jan Kara
2026-06-18 1:43 ` Zhang Yi
2026-05-11 7:23 ` [PATCH v4 07/23] ext4: do not use data=ordered mode for inodes using buffered iomap path Zhang Yi
2026-05-19 10:41 ` Ojaswin Mujoo
2026-05-19 13:31 ` Ojaswin Mujoo
2026-05-20 8:18 ` Zhang Yi
2026-05-20 13:17 ` Ojaswin Mujoo
2026-06-16 10:01 ` Jan Kara
2026-05-11 7:23 ` [PATCH v4 08/23] ext4: implement buffered write path using iomap Zhang Yi
2026-05-26 17:10 ` Ojaswin Mujoo
2026-05-28 15:40 ` Darrick J. Wong
2026-06-02 7:02 ` Ojaswin Mujoo
2026-05-29 9:13 ` Zhang Yi
2026-06-02 10:05 ` Ojaswin Mujoo
2026-06-03 1:44 ` Zhang Yi
2026-06-02 10:26 ` Ojaswin Mujoo
2026-06-03 2:56 ` Zhang Yi
2026-06-03 11:08 ` Ojaswin Mujoo
2026-06-16 10:45 ` Jan Kara
2026-06-16 14:42 ` Zhang Yi
2026-05-11 7:23 ` [PATCH v4 09/23] ext4: implement writeback " Zhang Yi
2026-05-27 12:49 ` Ojaswin Mujoo
2026-05-29 14:12 ` Zhang Yi
2026-05-29 15:32 ` Ojaswin Mujoo
2026-05-30 1:21 ` Zhang Yi
2026-06-01 6:26 ` Ojaswin Mujoo
2026-06-16 11:47 ` Jan Kara
2026-06-22 12:36 ` Zhang Yi
2026-06-24 17:16 ` Jan Kara
2026-06-25 3:33 ` Zhang Yi
2026-06-25 12:58 ` Jan Kara
2026-06-30 4:41 ` Zhang Yi
2026-05-11 7:23 ` [PATCH v4 10/23] ext4: implement mmap " Zhang Yi
2026-05-27 12:56 ` Ojaswin Mujoo
2026-06-16 11:56 ` Jan Kara
2026-05-11 7:23 ` [PATCH v4 11/23] iomap: correct the range of a partial dirty clear Zhang Yi
2026-05-11 7:46 ` Christoph Hellwig
2026-05-11 8:57 ` Zhang Yi
2026-05-11 7:23 ` [PATCH v4 12/23] iomap: support invalidating partial folios Zhang Yi
2026-05-11 7:23 ` [PATCH v4 13/23] iomap: fix incorrect did_zero setting in iomap_zero_iter() Zhang Yi
2026-05-11 7:23 ` [PATCH v4 14/23] ext4: implement partial block zero range path using iomap Zhang Yi
2026-05-27 13:13 ` Ojaswin Mujoo
2026-06-16 12:28 ` Jan Kara
2026-06-17 8:14 ` Zhang Yi
2026-06-17 10:50 ` Jan Kara
2026-06-17 13:00 ` Zhang Yi
2026-06-17 10:56 ` Brian Foster
2026-06-17 13:22 ` Zhang Yi
2026-05-11 7:23 ` [PATCH v4 15/23] ext4: add block mapping tracepoints for iomap buffered I/O path Zhang Yi
2026-05-27 13:14 ` Ojaswin Mujoo
2026-06-16 12:29 ` Jan Kara
2026-05-11 7:23 ` [PATCH v4 16/23] ext4: disable online defrag when inode using " Zhang Yi
2026-05-27 13:14 ` Ojaswin Mujoo
2026-06-16 12:30 ` Jan Kara
2026-05-11 7:23 ` [PATCH v4 17/23] ext4: submit zeroed post-EOF data immediately in the " Zhang Yi
2026-05-27 13:41 ` Ojaswin Mujoo
2026-05-30 2:53 ` Zhang Yi
2026-06-01 9:08 ` Ojaswin Mujoo
2026-06-01 12:22 ` Zhang Yi
2026-06-01 18:15 ` Ojaswin Mujoo
2026-06-02 3:36 ` Zhang Yi
2026-06-18 12:59 ` Jan Kara
2026-06-22 3:37 ` Zhang Yi
2026-05-11 7:23 ` [PATCH v4 18/23] ext4: wait for ordered I/O " Zhang Yi
2026-05-27 15:58 ` Ojaswin Mujoo
2026-05-28 13:34 ` Ojaswin Mujoo
2026-05-30 9:32 ` Zhang Yi
2026-06-02 5:56 ` Ojaswin Mujoo
2026-05-30 7:22 ` Zhang Yi
2026-05-30 8:24 ` Zhang Yi
2026-06-01 18:33 ` Ojaswin Mujoo
2026-06-02 3:22 ` Zhang Yi
2026-06-02 5:35 ` Ojaswin Mujoo
2026-06-18 13:48 ` Jan Kara
2026-06-22 3:32 ` Zhang Yi
2026-06-25 9:42 ` Jan Kara
2026-06-30 12:06 ` Zhang Yi [this message]
2026-07-02 14:20 ` Jan Kara
2026-07-04 2:34 ` Zhang Yi
2026-05-11 7:23 ` [PATCH v4 19/23] ext4: update i_disksize to i_size on ordered I/O completion Zhang Yi
2026-05-11 7:23 ` [PATCH v4 20/23] ext4: wait for ordered I/O to complete during insert and collapse range Zhang Yi
2026-05-11 7:23 ` [PATCH v4 21/23] ext4: add tracepoints for ordered I/O in the iomap buffered I/O path Zhang Yi
2026-05-11 7:23 ` [PATCH v4 22/23] ext4: partially enable iomap for the buffered I/O path of regular files Zhang Yi
2026-05-11 7:23 ` [PATCH v4 23/23] ext4: introduce a mount option for iomap buffered I/O path Zhang Yi
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=8999aa70-073c-47f9-ae6e-39af3cc1fe32@gmail.com \
--to=yizhang089@gmail.com \
--cc=adilger.kernel@dilger.ca \
--cc=djwong@kernel.org \
--cc=hch@infradead.org \
--cc=jack@suse.cz \
--cc=libaokun@linux.alibaba.com \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ojaswin@linux.ibm.com \
--cc=ritesh.list@gmail.com \
--cc=tytso@mit.edu \
--cc=yangerkun@huawei.com \
--cc=yi.zhang@huawei.com \
--cc=yi.zhang@huaweicloud.com \
--cc=yukuai@fnnas.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.