From e99bce558ddcc98e2495280e1af0dee372fae619 Mon Sep 17 00:00:00 2001 From: Baolin Liu Date: Wed, 9 Oct 2024 09:30:34 +0800 Subject: [PATCH v1] ext4: fix a assertion failure due to ungranted bh dirting Since the merge of commit 3910b513fcdf ("ext4: persist the new uptodate buffers in ext4_journalled_zero_new_buffers"), a new assertion failure occurred under a old kernel(ext3, data=journal, pagesize=64k) with corresponding ported patches: ================================================================ Call trace: __ext4_handle_dirty_metadata+0x320/0x7e8 write_end_fn+0x78/0x178 ext4_journalled_zero_new_buffers+0xd0/0x2c8 ext4_block_write_begin+0x850/0xc00 ext4_write_begin+0x334/0xc68 generic_perform_write+0x1a4/0x380 ext4_buffered_write_iter+0x180/0x370 ext4_file_write_iter+0x194/0xfc0 new_sync_write+0x338/0x4b8 __vfs_write+0xc4/0xe8 vfs_write+0x12c/0x3d0 ksys_write+0xf4/0x230 sys_write+0x34/0x48 el0_svc_naked+0x44/0x48 ================================================================ which was caused by bh dirting without calling do_journal_get_write_access(). In the loop for all bhs of a page in ext4_block_write_begin(), when a err occurred, it will jump out of loop. But that will leaves some bhs being processed and some not, which will lead to the asserion failure in calling write_end_fn(). To fixed that, get write access for the rest unprocessed bhs, just as what write_end_fn do. Fixes: 3910b513fcdf ("ext4: persist the new uptodate buffers in ext4_journalled_zero_new_buffers") Reported-and-tested-by: Zhi Long Suggested-by: Shida Zhang Signed-off-by: Baolin Liu --- fs/ext4/inode.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 54bdd4884fe6..a72f951288e4 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1102,9 +1102,24 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio, err = -EIO; } if (unlikely(err)) { - if (should_journal_data) + if (should_journal_data) { + if (bh != head || !block_start) { + do { + block_end = block_start + bh->b_size; + + if (buffer_new(bh)) + if (block_end > from && block_start < to) + do_journal_get_write_access(handle, + inode, bh); + + block_start = block_end; + bh = bh->b_this_page; + } while (bh != head); + } + ext4_journalled_zero_new_buffers(handle, inode, folio, from, to); + } else folio_zero_new_buffers(folio, from, to); } else if (fscrypt_inode_uses_fs_layer_crypto(inode)) { -- 2.39.2