]> www.infradead.org Git - users/hch/misc.git/commitdiff
ext4: wait for ongoing I/O to complete before freeing blocks
authorZhang Yi <yi.zhang@huawei.com>
Tue, 16 Sep 2025 09:33:37 +0000 (17:33 +0800)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 10 Oct 2025 17:34:35 +0000 (13:34 -0400)
When freeing metadata blocks in nojournal mode, ext4_forget() calls
bforget() to clear the dirty flag on the buffer_head and remvoe
associated mappings. This is acceptable if the metadata has not yet
begun to be written back. However, if the write-back has already started
but is not yet completed, ext4_forget() will have no effect.
Subsequently, ext4_mb_clear_bb() will immediately return the block to
the mb allocator. This block can then be reallocated immediately,
potentially causing an data corruption issue.

Fix this by clearing the buffer's dirty flag and waiting for the ongoing
I/O to complete, ensuring that no further writes to stale data will
occur.

Fixes: 16e08b14a455 ("ext4: cleanup clean_bdev_aliases() calls")
Cc: stable@kernel.org
Reported-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Closes: https://lore.kernel.org/linux-ext4/a9417096-9549-4441-9878-b1955b899b4e@huaweicloud.com/
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Message-ID: <20250916093337.3161016-3-yi.zhang@huaweicloud.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/ext4_jbd2.c

index b3e9b7bd797879ac0bd92ea12005cdd2a37032c2..a0e66bc1009308a14460ad5984b610edab1d6fd0 100644 (file)
@@ -280,9 +280,16 @@ int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
                  bh, is_metadata, inode->i_mode,
                  test_opt(inode->i_sb, DATA_FLAGS));
 
-       /* In the no journal case, we can just do a bforget and return */
+       /*
+        * In the no journal case, we should wait for the ongoing buffer
+        * to complete and do a forget.
+        */
        if (!ext4_handle_valid(handle)) {
-               bforget(bh);
+               if (bh) {
+                       clear_buffer_dirty(bh);
+                       wait_on_buffer(bh);
+                       __bforget(bh);
+               }
                return 0;
        }