[PATCH -next v5 27/32] ext4: clear DISKSIZE_GROW_PENDING on truncate or error
Zhang Yi <[email protected]>
| Newsgroups | org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-ext4,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Zhang Yi <[email protected]> The disksize-grow-pending state is set when a zeroed EOF block is queued for writeback and cleared by the ioend completion path once writeback finishes. However, the zeroed block may be discarded before writeback completes — through folio discard, truncate, or unlink and inode eviction. Additionally, if the filesystem enters an emergency state, the block will no longer be written back. In any of these cases, leaving the bit set would block subsequent writeback indefinitely. Therefore, we must clear it on all paths that invalidate the pending block before writeback completes: - ext4_iomap_discard_folio() on folio discard. - ext4_evict_inode() when an unlinked inode is destroyed. - ext4_iomap_writepages() when the filesystem is in emergency state. In ext4_truncate_down(), truncating past the pending zeroed EOF block also invalidates the pending disksize update, so the bit must be cleared there as well. Finally, add a WARN_ON in ext4_destroy_inode() to catch any inode destroyed with the bit still set. Signed-off-by: Zhang Yi <[email protected]> --- fs/ext4/inode.c | 33 +++++++++++++++++++++++++++++++-- fs/ext4/super.c | 19 +++++++++++++------ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 056937e27859..a1dfb70127ca 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -273,6 +273,8 @@ void ext4_evict_inode(struct inode *inode) if (ext4_should_order_data(inode)) ext4_begin_ordered_truncate(inode, 0); + if (ext4_inode_buffered_iomap(inode)) + ext4_iomap_clear_disksize_pending(inode); truncate_inode_pages_final(&inode->i_data); /* @@ -4344,8 +4346,17 @@ static void ext4_iomap_discard_folio(struct folio *folio, loff_t pos) { struct inode *inode = folio->mapping->host; loff_t length = folio_pos(folio) + folio_size(folio) - pos; + loff_t pstart, plen; ext4_iomap_punch_delalloc(inode, pos, length, NULL); + + /* + * Clear the disksize-grow-pending state if the zeroed EOF block + * fails to write back and is discarded. + */ + plen = ext4_iomap_get_disksize_pending_range(inode, &pstart); + if (plen && pos <= pstart && folio_next_pos(folio) >= pstart + plen) + ext4_iomap_clear_disksize_pending(inode); } static ssize_t ext4_iomap_writeback_range(struct iomap_writepage_ctx *wpc, @@ -4453,8 +4464,15 @@ static int ext4_iomap_writepages(struct address_space *mapping, }; ret = ext4_emergency_state(sb); - if (unlikely(ret)) + if (unlikely(ret)) { + /* + * The filesystem is in an emergency state and no further + * writeback will occur. Clear the disksize-grow-pending + * state to avoid complaints when the inode is destroyed. + */ + ext4_iomap_clear_disksize_pending(inode); return ret; + } /* * Submit the pending zeroed EOF block range if the entire @@ -6741,7 +6759,18 @@ static int ext4_truncate_down(struct inode *inode, loff_t oldsize, start_lblk = newsize > 0 ? (newsize - 1) >> inode->i_blkbits : 0; ext4_fc_track_range(handle, inode, start_lblk, EXT_MAX_BLOCKS - 1); - ext4_set_inode_size(inode, newsize); + down_write(&EXT4_I(inode)->i_data_sem); + /* + * Truncate the zeroed EOF block invalidates the pending disksize + * update, so clear the disksize-grow-pending state. + */ + if (ext4_test_inode_state(inode, EXT4_STATE_DISKSIZE_GROW_PENDING) && + (newsize <= EXT4_I(inode)->i_disksize)) + ext4_iomap_clear_disksize_pending(inode); + + i_size_write(inode, newsize); + __ext4_set_i_disksize(inode, newsize); + up_write(&EXT4_I(inode)->i_data_sem); ret = ext4_mark_inode_dirty(handle, inode); ext4_journal_stop(handle); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 1c2395aa1d53..86ed5228dbe9 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1491,12 +1491,19 @@ static void ext4_destroy_inode(struct inode *inode) dump_stack(); } - if (!(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ERROR_FS) && - WARN_ON_ONCE(EXT4_I(inode)->i_reserved_data_blocks)) - ext4_msg(inode->i_sb, KERN_ERR, - "Inode %llu (%p): i_reserved_data_blocks (%u) not cleared!", - inode->i_ino, EXT4_I(inode), - EXT4_I(inode)->i_reserved_data_blocks); + if (!(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ERROR_FS)) { + if (WARN_ON_ONCE(EXT4_I(inode)->i_reserved_data_blocks)) + ext4_msg(inode->i_sb, KERN_ERR, + "Inode %llu (%p): i_reserved_data_blocks (%u) not cleared!", + inode->i_ino, EXT4_I(inode), + EXT4_I(inode)->i_reserved_data_blocks); + + if (WARN_ON_ONCE(ext4_test_inode_state(inode, + EXT4_STATE_DISKSIZE_GROW_PENDING))) + ext4_msg(inode->i_sb, KERN_ERR, + "Inode %llu (%p): EXT4_STATE_DISKSIZE_GROW_PENDING not cleared!", + inode->i_ino, EXT4_I(inode)); + } } static void ext4_shutdown(struct super_block *sb) -- 2.52.0