summaryrefslogtreecommitdiff
path: root/fs/ext4/ext4.h
AgeCommit message (Collapse)Author
8 daysMerge tag 'ext4_for_linus-7.4-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: "Add support for buffered iomap support in ext4. This is not enabled by default, but requires the buffered_iomap mount option, as this mode does not support online defragmentation, inline data, fsverify, fscrypt, non-extent inodes, and data=journal mode. If buffered_iomap is enabled, ext4 will fall back to the original I/O code path automatically if necessary. Large writes show significant performance improvements when buffered iomap is enabled. There are also miscellaneous bug fixes and improvements, including for fast commit and avoiding deadlocks and out-of-bound reads when handling corner cases involving corrupted file systems" * tag 'ext4_for_linus-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (41 commits) ext4: introduce a mount option for iomap buffered I/O path ext4: partially enable iomap for the buffered I/O path of regular files ext4: add tracepoints for EOF block zeroing and disksize-grow I/O ext4: add tracepoints for DISKSIZE_GROW_PENDING set, clear, and wait ext4: set DISKSIZE_GROW_PENDING after zeroing unaligned EOF block ext4: clear DISKSIZE_GROW_PENDING on truncate or error ext4: submit and wait for disksize-grow I/O in fallocate paths ext4: defer i_disksize update while DISKSIZE_GROW_PENDING is set ext4: advance i_disksize to i_size upon disksize-grow I/O completion ext4: submit and wait for pending disksize-grow I/O on writeback ext4: add EXT4_STATE_DISKSIZE_GROW_PENDING state bit and helpers ext4: disable online defrag when inode using iomap buffered I/O path ext4: add block mapping tracepoints for iomap buffered I/O path ext4: drain writeback before removing extents on the iomap path ext4: implement partial block zero range path using iomap ext4: implement mmap path using iomap ext4: implement writeback path using iomap ext4: rework handle credit accounting for unwritten extent conversion ext4: implement buffered write path using iomap ext4: do not use data=ordered mode for inodes using buffered iomap path ...
2026-10-19Merge tag 'vfs-7.4-rc1.idmap' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs idmap constification from Christian Brauner: "This makes struct mnt_idmap const throughout the tree. A mount's idmapping is immutable. The only thing modified after alloc_mnt_idmap() is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only reads from it. That is the same model struct cred uses and the idmapping is sensitive enough to deserve it. The conversion goes bottom up through the vfs{g,u}id and fs{g,u}id helpers, the privilege checks, acl, xattr and quota and then every inode operation that takes an idmap, so ->permission(), ->getattr(), ->setattr(), ->create(), ->mkdir(), ->mknod(), ->symlink(), ->rename(), ->tmpfile(), ->get_acl(), ->set_acl() and ->fileattr_set() with their implementations in every filesystem, plus the LSM hooks. struct vfsmount, struct mount_kattr and struct kstatmount store a const pointer and nop_mnt_idmap and invalid_mnt_idmap live in rodata, so anything that tries to modify them faults instead of silently corrupting the initial idmapping" * tag 'vfs-7.4-rc1.idmap' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (27 commits) fs: make nop_mnt_idmap and invalid_mnt_idmap const fs: port mnt_idmap() and file_mnt_idmap() to const mnt_idmap fs: port vfs_*() helpers to const mnt_idmap fs: port ->setattr() to pass const mnt_idmap fs: port ->getattr() to pass const mnt_idmap fs: port ->create() to pass const mnt_idmap fs: port ->symlink() to pass const mnt_idmap fs: port ->mkdir() to pass const mnt_idmap fs: port ->rename() to pass const mnt_idmap fs: port ->mknod() to pass const mnt_idmap fs: port ->tmpfile() to pass const mnt_idmap fs: port ->get_acl() to pass const mnt_idmap fs: port ->set_acl() to pass const mnt_idmap fs: port ->fileattr_set() to pass const mnt_idmap fs: port xattr to const mnt_idmap fs: port ->permission() to pass const mnt_idmap fs: port acl to const mnt_idmap fs: port inode_init_owner() to const mnt_idmap fs: port inode_owner_or_capable() to const mnt_idmap fs: port privilege checking helpers to const mnt_idmap ...
2026-10-15ext4: introduce a mount option for iomap buffered I/O pathZhang Yi
Since the iomap buffered I/O path does not yet support all existing ext4 features, it cannot be enabled by default. Introduce the 'buffered_iomap' and 'nobuffered_iomap' mount options to explicitly enable or disable the iomap buffered I/O path for regular files. Toggling this option via remount is allowed. The change of I/O path will not take effect immediately. It will be deferred. The new setting will only take effect after the inode is re-initialized (i.e., after the last reference is dropped and the inode is re-read from disk). Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103831.2978767-3-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: partially enable iomap for the buffered I/O path of regular filesZhang Yi
Introduce ext4_enable_buffered_iomap() to determine whether a regular file inode should use the iomap buffered I/O path. We now support the default filesystem features, mount options, and the bigalloc feature. However, inline data, fsverity, fscrypt, indirect inode type, and data=journal mode are not fully supported. The decision is made at inode initialization time in __ext4_new_inode() and __ext4_iget() by setting the EXT4_STATE_BUFFERED_IOMAP state flag. If any of these unsupported features are met, the inode silently falls back to the traditional buffer_head path. Switching the buffered I/O path on an active inode is not supported, with the exception of changing a per-inode journal flag. For features like encryption, verity, and inline data that can be dynamically enabled at the superblock level, checking the global feature flag avoids the complexity of toggling the path on individual inodes. Additionally: - Extend ext4_inode_journal_mode() to force ordered mode for inodes using the iomap path under a data=journal mount. For the global data journal mode (EXT4_MOUNT_JOURNAL_DATA), dynamic enablement is deferred until the next inode re-initialization. For the per-inode data journal mode (EXT4_INODE_JOURNAL_DATA), dynamic changes take effect immediately, as it is safe to switch address_space operations and drop all page cache under i_rwsem and filemap_invalidate_lock. - Add a WARN_ON_ONCE() guard in _ext4_get_block() to catch inodes using the iomap path from accidentally entering the legacy buffer_head writeback path. - Place silent BUFFERED_IOMAP checks in ext4_do_writepages() and ext4_iomap_writepages() under the writepages rwsem. Unlike _ext4_get_block(), the writeback path does not hold i_rwsem or invalidate_lock and can race with ext4_change_inode_journal_flag() switching the inode's a_ops. - Reject extent-to-indirect migration via ext4_ind_migrate() for inodes on the iomap path. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103831.2978767-2-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: submit and wait for disksize-grow I/O in fallocate pathsZhang Yi
Collapse range and insert range update i_disksize directly under i_data_sem. If the operation runs while the zeroed EOF block is still awaiting writeback, i_disksize could advance past the zeroed boundary before the zeroed data is persisted, exposing stale data on crash. Deferring i_disksize updates like fallocate and zero_range is not an option here because the shift would move written extents beyond the current i_disksize. So flush and wait for the pending zeroed EOF block before these operations advance i_disksize. Since these operations already perform writeback, the extra flush does not add significant overhead. In addition, for ext4_update_disksize_before_punch(), if the punch discards the pending block, the zeroed data will never be written back before advancing i_disksize, so it is also necessary to sync the pending EOF range there. Finally, for the SYNC variants of zero_range and fallocate, this also guarantees the i_disksize update is persisted on the synchronous return. Note that EXT4_STATE_DISKSIZE_GROW_PENDING is not set for now, and it will be set after everything is done. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103033.2920530-26-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: defer i_disksize update while DISKSIZE_GROW_PENDING is setZhang Yi
Operations like append allocate, zero range, and truncate update i_disksize directly. If the new i_disksize exceeds the original value while the zeroed EOF block is still awaiting writeback, metadata may be persisted before the zeroed data, exposing stale data on crash. Defer i_disksize updates while EXT4_STATE_DISKSIZE_GROW_PENDING is set; the ioend worker for the pending block will advance i_disksize to i_size once the zeroed data is written back. The tradeoff is that i_disksize may lag i_size transiently, but this is observable only to callers that read i_disksize directly. Introduce __ext4_set_i_disksize() to centralize the bit check for callers already holding i_data_sem (ext4_ext_truncate and ext4_set_inode_size), and refactor ext4_update_inode_size() to take i_data_sem itself and check the bit atomically with i_size_write(), so the ioend worker observes the latest i_size under the same lock. Note that in the O_SYNC mode of ext4_do_fallocate() or ext4_zero_range(), we need to flush out pending blocks that can update i_disksize synchronously. This will be handled later, and EXT4_STATE_DISKSIZE_GROW_PENDING will also be set after everything is done. Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103033.2920530-25-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: submit and wait for pending disksize-grow I/O on writebackZhang Yi
When the current writeback pass begins beyond the disksize-grow-pending zeroed EOF block, the ioend worker would otherwise have to wait for the pending EOF block to complete before it can advance i_disksize. Otherwise the old EOF block could be exposed as stale data once i_disksize advances past it. Therefore, introduce the ioend mechanism for the pending range, tag ioends that cover the pending zeroed EOF block which straddles i_disksize with EXT4_IOMAP_IOEND_DISKSIZE_GROW_IO in ext4_iomap_writeback_submit(), and clear the bit and wake up all waiters in ext4_iomap_end_bio() when such an ioend completes. Clearing EXT4_IOMAP_IOEND_DISKSIZE_GROW_IO does not depend on whether the disksize grow I/O succeeds. That is, even if the I/O fails, we still allow subsequent writes in the range to update i_disksize. This is consistent with the previous behavior, and we rely on data_err=abort to prevent metadata updates when data write failures occur. In order to avoid the ioend that passes the pending range waiting for a long time, proactively submit the pending range first in ext4_iomap_writepages() so it completes in parallel with the rest of the writeback. Note that the handling of discarding the zeroed EOF folio will be processed later, otherwise the bit will be set forever. EXT4_STATE_DISKSIZE_GROW_PENDING will be set after everthing is done. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103033.2920530-23-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: add EXT4_STATE_DISKSIZE_GROW_PENDING state bit and helpersZhang Yi
Inodes using the iomap buffered I/O path do not use data=ordered mode, so the zeroed EOF block that straddles i_disksize needs explicit tracking to ensure it is written back before i_disksize is advanced. Add the EXT4_STATE_DISKSIZE_GROW_PENDING inode state bit and three helpers: ext4_iomap_clear_disksize_pending() to atomically clear the bit and wake waiters, ext4_iomap_wait_disksize_pending() to block until the bit is cleared, and ext4_iomap_get_disksize_pending_range() to compute the pending range from i_disksize. These will be used by subsequent patches to serialize i_disksize updates with the writeback of the zeroed EOF block. Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103033.2920530-22-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: implement writeback path using iomapZhang Yi
Add the iomap writeback path for ext4 buffered I/O. This introduces: - ext4_iomap_writepages(): the main writeback entry point. - ext4_writeback_ops: a new iomap_writeback_ops instance to handle block mapping and I/O submission. - A new end I/O work handler ext4_iomap_finish_ioend() is added for converting unwritten extents, updating file size, and handling DATA_ERR_ABORT after I/O completion. Core implementation details: - ->writeback_range() callback Calls ext4_iomap_map_writeback_range() to map and allocate blocks using the enhanced ext4_map_blocks(). ext4_map_blocks() now starts its own transaction internally when it needs to allocate blocks. For performance, when a block range is not yet allocated, it allocates based on the writeback length and delalloc extent length (ext4_map_blocks() will trim the allocated length), rather than allocating a single folio at a time. The folio is then added to an iomap_ioend instance. - ->writeback_submit() callback Registers ext4_iomap_end_bio() as the end bio callback. This callback schedules a worker to handle: - Unwritten extent conversion. - i_disksize update after data is written back. - Journal abort on writeback I/O failure. - The new end I/O work handler ext4_iomap_finish_ioend() This reuses i_rsv_conversion_work and i_rsv_conversion_list, which are now used in the buffer_head writeback path for memory saving, because an inode can never go through both buffer_head and iomap paths. Those are not initialized in this patch but will be when the iomap path is formally enabled later. Key changes and considerations: - Append write and unwritten extents Since data=ordered mode is not used to prevent stale data exposure during append writebacks, new blocks are always allocated as unwritten extents (i.e. always enable dioread_nolock), and i_disksize update is postponed until I/O completion. Additionally, the deadlock that the reserve handle was expected to resolve does not occur anymore. Therefore, the end I/O worker can start a normal journal handle instead of a reserve handle when converting unwritten extents. - Lock ordering The ->writeback_range() callback runs under the folio lock, requiring the journal handle to be started under that same lock. This reverses the order compared to the buffer_head writeback path. The lock ordering documentation in super.c has been updated accordingly. - Don't cache writes The iomap infrastructure sets the BIO_COMPLETE_IN_TASK flag when submitting I/O, so the ioend will be processed in task context. However, if a private defer worker is to be started, this flag must be cleared explicitly to avoid double deferral. In the future, all private defer work should be moved to the generic bio complete in task framework. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103033.2920530-16-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: implement buffered write path using iomapZhang Yi
Introduce two new iomap_ops instances for ext4 buffered writes: - ext4_iomap_buffered_da_write_ops: for delayed allocation mode, using ext4_da_map_blocks() to map delalloc extents. - ext4_iomap_buffered_write_ops: for non-delayed allocation mode, using ext4_map_blocks() to directly allocate blocks. Also add ext4_iomap_valid() for the iomap infrastructure to check extent validity. This is useful for the iomap buffered write path because it does not hold the folio lock between mapping the inode extent and copying data. So it can race with writeback, which may modify or stale the extent type. Key changes and considerations: - Unwritten extents for new blocks (dioread_nolock always on) Since data=ordered mode is not used to prevent stale data exposure in the non-delayed allocation path, new blocks are always allocated as unwritten extents. - Short write and write failure handling a. Delalloc path: On short write or failure, the stale delalloc range must be dropped and its space reservation released. Otherwise, a clean folio may cover leftover delalloc extents, causing inaccurate space reservation accounting. b. Non-delalloc path: No cleanup of allocated blocks is needed on short write since leaving the preallocated blocks behind doesn't cause any real issue. These blocks are visible to users and are not permanently leaked. - Adjust the call frequency of ext4_nonda_switch() so that it is no longer called once per folio in the buffer_head path, but instead once per write. For code simplicity, ext4_iomap_buffered_da_write_ops will be shared with the later page-fault path. Since ext4_nonda_switch() -> try_to_writeback_inodes_sb() synchronously waits for writeback, which could lead to deadlock under the same folio lock acquired by iomap_page_mkwrite(), we can no longer call ext4_nonda_switch() inside ext4_iomap_buffered_da_write_ops. This may increase the probability of a writeback-time allocation failure, but the expected real-world impact is small, so it can be implemented this way for now. - Lock ordering reversal The folio lock and transaction start ordering is reversed compared to the buffer_head buffered write path. To handle this, the journal handle must be stopped in iomap_begin() callbacks. The lock ordering documentation in super.c has been updated accordingly. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103033.2920530-14-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-15ext4: add iomap address space operations for buffered I/OZhang Yi
Introduce initial support for iomap in the buffered I/O path for regular files on ext4. - Add a new inode state flag EXT4_STATE_BUFFERED_IOMAP to indicate the inode uses iomap instead of buffer_head for buffered I/O - Add helper ext4_inode_buffered_iomap() to check the flag - Add new address space operations ext4_iomap_aops with callbacks that will use generic iomap implementations - Add ext4_iomap_aops to ext4_set_aops() when the flag is set The following callbacks(read_folio(), readahead(), writepages()) are provided as placeholders and will be implemented in later patches. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com> Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20261009103033.2920530-10-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-14ext4: free the sbi after a grace periodChristian Brauner
ext4_get_link() follows a symlink that keeps its target in a data block in rcu pathwalk mode with ext4_getblk(EXT4_GET_BLOCKS_CACHED_NOWAIT). That reads s_mount_state in the ASSERT() of ext4_getblk() and again in ext4_es_lookup_extent(), and the lookup bumps the extent status hit or miss counter, all through sb->s_fs_info. ext4_put_super() destroys those counters, clears sb->s_fs_info and frees the sbi without waiting for a grace period. A pathwalk that is still in ext4_get_link() when the last reference to the mount goes away then dereferences a NULL s_fs_info, the freed sbi or the destroyed percpu counters. With a pathwalk held up in ext4_get_link() while the last reference goes away: KASAN: null-ptr-deref in range [0x00000000000000a8-0x00000000000000af] RIP: 0010:ext4_getblk+0xf0/0x640 Call Trace: <TASK> ext4_get_link+0x26c/0x410 pick_link+0xf96/0x1780 step_into_slowpath+0x9b8/0x1250 path_lookupat+0x484/0xb00 filename_lookup+0x1f1/0x5b0 vfs_statx+0xd7/0x3b0 vfs_fstatat+0x4d/0x90 __do_sys_newfstatat+0xa0/0x100 do_syscall_64+0x10e/0x5e0 entry_SYSCALL_64_after_hwframe+0x77/0x7f So far that has been hard to hit. umount(2) waits for a grace period in namespace_unlock() before it drops the mount, so only a mount whose last reference goes away elsewhere, like the close of a file or a chdir out of a lazily unmounted directory, can race a walker. Keep sb->s_fs_info and free the sbi together with the extent status counters after a grace period. Split the counters out of ext4_es_unregister_shrinker() for that. The shrinker still gets wasted in ext4_put_super(), the counters go with the sbi. Fixes: 6493792d3299 ("ext4: convert symlink external data block mapping to bdev") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20261009-work-fixes-rcu-v1-1-7446d2d335e6@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-10-08ext4: convert group-count barrier protocol to acquire/releaseJinjie Ruan
During an online resize, reading s_groups_count and s_blockfile_groups requires strict ordering against subsequent initialized block group metadata. Replace the historical smp_wmb()/smp_rmb() pairs with smp_store_release() and smp_load_acquire(). This formalizes the publish-subscribe pattern and allows weakly-ordered architectures (e.g., arm64) to utilize native STLR/LDAR instructions instead of full DMB fences. Update the documentation in resize.c and ext4.h accordingly. Cc: "Theodore Ts'o" <tytso@mit.edu> Cc: Andreas Dilger <adilger.kernel@dilger.ca> Cc: Baokun Li <libaokun@linux.alibaba.com> Cc: Jan Kara <jack@suse.cz> Cc: Ojaswin Mujoo <ojaswin@linux.ibm.com> Cc: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com> Cc: Zhang Yi <yi.zhang@huawei.com> Assisted-by: Gemini:Gemini-3.1 Assisted-by: DeepSeek:DeepSeek-V3 Reviewed-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com> Link: https://patch.msgid.link/20260902074805.398540-9-ruanjinjie@huawei.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-10-08ext4: don't report delalloc data as a hole on indirect-mapped inodesDaejun Park
When a plain lookup finds no block, ext4_ind_map_blocks() sizes the hole it reports by counting the empty subtrees under 'partial' in the on-disk indirect tree. That count knows nothing about delayed allocation, so a range holding delalloc data that has not been written back yet is reported as a plain hole. The extent-mapped path does not have this problem: it trims the hole it found at the first delayed extent before returning it. ext4_map_blocks() consults the extent status tree before it calls into the mapping layer, so a query that starts exactly on the delayed block still finds it. A query that starts earlier does not, because the hole reported for the earlier block already spans the delayed one. On a 4k-block filesystem, lseek(SEEK_DATA) from offset 0 on a file whose only data block is at logical block N and is still dirty returns: N = 0..11 direct blocks N << 12 N = 12 first single-indirect block N << 12 N = 13..1035 inside that indirect block -1 ENXIO N = 1036 first double-indirect block N << 12 Blocks 12 and 1036 survive because the query lands on the delayed block itself. Blocks 13..1035 are swallowed by the 1024-block hole reported for block 12. fiemap loses the same data for the same reason. An ext3 filesystem mounted as ext4 hits this during ordinary use: ext3 inodes stay indirect mapped, while mount -t ext4 turns on delayed allocation even though mounting ext3 with -o delalloc is explicitly rejected. install(1) from coreutils uses SEEK_DATA to locate data in its source file, so installing a sparse file that has not been written back silently produces a destination of the right size holding none of the data: truncate -s 1G img && mkfs.ext3 -F img && mount -t ext4 img mnt echo | dd of=mnt/src bs=1 count=1 seek=64K install mnt/src mnt/dst cmp mnt/src mnt/dst # differ: char 65537 Reconciling a hole with the extent status tree does not depend on how the hole was found, so instead of open coding it a second time, split the delalloc handling out of ext4_ext_determine_insert_hole() into ext4_determine_insert_hole(), which takes the hole the caller located, and call that from ext4_ind_map_blocks() as well. All the special cases and the comments explaining them stay in one place. Two things change for indirect-mapped inodes. They now cache the holes they report, as the extent-mapped path already does: ext4_punch_hole() already puts EXTENT_STATUS_HOLE on such inodes, ext4_map_blocks() and ext4_da_map_blocks() already handle a cached hole, the latter by replacing it with the delayed extent, and consecutive hole entries merge, so the tree does not grow with the length of a scan. And when a delayed extent covers the queried block itself - a race, since ext4_map_blocks() found nothing in the tree just before - the hole is now reported only up to the end of that extent instead of in full. The indirect path passes hole_start == lblk, so the "delalloc extent in front of the queried range" case cannot be reached there: __es_tree_search() never returns an extent ending before the block it was searched for. With this the sweep above returns N << 12 for every N, while the nodelalloc and extent-mapped controls are unchanged. generic/225, generic/285, generic/448 and generic/490 pass on ext4 made both with and without the extent feature. generic/286 and generic/436 pass on extent-mapped ext4 and are skipped without it, since they need fallocate. Fixes: facab4d9711e ("ext4: return hole from ext4_map_blocks()") Reported-by: Alexander Monakov <amonakov@ispras.ru> Closes: https://lore.kernel.org/linux-ext4/594c17d9-c00f-e485-96fb-cedf27ce3aa3@ispras.ru/ Suggested-by: Jan Kara <jack@suse.cz> Cc: stable@vger.kernel.org Signed-off-by: Daejun Park <daejun7.park@samsung.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Zhang Yi <yi.zhang@huawei.com> Link: https://patch.msgid.link/20260827084829epcms2p6ecc0c39a71d0c3eebf9cb1ade77314f6@epcms2p6 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-09-23fs: port ->setattr() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-24-54ccd48e100b@kernel.org Acked-by: Paul Moore <paul@paul-moore.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->getattr() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-23-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->fileattr_set() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-14-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port inode_init_owner() to const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-10-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-08-20Merge tag 'ext4_for_linus-7.3-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: - Improve performance by allowing parallel DIO writes when we were previously being overly conservative when checking whether it was safe to avoid requiring an exclusive lock - Improve the performance of ext4_mb_prefetch() used by fallocate() by avoiding work when it is not needed - Remove the unnecessary custom end_io function ext4_end_buffer_io_sync() - Improve performance when performing an overwrite to an already uptodate folio - Clean up how we handle deallocating EA inodes to avoid a potential lock ordering issue when there is a failed mount while an EA inode is still being evicted - Use str_plural() instead of a custom macro - Avoid soft lockups or RCU stalls if there are many busy buffers (caused by heavy I/O) while checkpointing - Use scoped NOFS when starting a handle in nojournal mode - Align fields in handle structure to optimize setting and getting the h_type and h_line_no fields - Fix documentation of the meta_bg block group layout - Bug fixes: - Fix a potential out-of-bounds read in ext4_read_inline_dir() - Fix a potential deadlock when concurrent xattr operations are racing with each other when some of the xattrs are using the ea_inode feature - Fix a spurious warning with data=journal that can be triggered when writeback races with remounting the file system read-only - Fix a potential deadlock when EXT4_IOC_MIGRATE races with a file system freeze operation - Make sure all in-flight direct I/O operations are complete before falling back to buffered I/O - Handle IOCB_NOWAIT properly when performing a extending DAX write - Prevent potentially sleeping on a block allocation when IOCB_NOWAIT is set - Fix potential races when racing an inline data write with a page fault - Propagate errors when adding or removing extent ranges during a fast commit replay - Avoid trying to expand an inode's extra size when it is being evicted to avoid a number of corner case or deadlocks - Avoid spurious error when retrying inode extra size expansion - Fix corner cases where we underestimate the number of journal credits needed - Avoid hangs/crashes/WARNINGS caused by maliciously corrupted file systems - Don't issue spurious orphan clean message on RO file systems - Avoid leaving the file system in an inconsistent state after a crash when a WRITE_ZEROS in progress converting an unwritten extent to a written extent - Handle WRITE_ZEROS correctly when there are some partially dirtied regions in the page cache - Pass errors during zero-rage, truncate, or punch hole to the caller if ext4_get_block() fails - Wait for writeback to finish when triggered by zero-range or zero-range for those devices that require stable writes - If the reserved gid superblock field is set, set the reserved gid instead of the reserved uid * tag 'ext4_for_linus-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (56 commits) ext4: fix estimate extent index blocks in ext4_ext_index_trans_blocks() ext4: fix transaction overflow during writeback ext4: teach ext4_meta_trans_blocks() about number of allocated extents ext4: guard against NULL s_group_info in ext4_get_group_info ext4: fix spurious message about orphan cleanup on RO fs ext4: stop retrying saturated xattr cache entries ext4: don't enable DAX on new encrypted files ext4: protect WRITE_ZEROES written extents with orphan list ext4: export converted block count from ext4_convert_unwritten_extents() ext4: fix incorrect function call when initializing s_resgid ext4: validate EA inode i_nlink in ext4_xattr_inode_iget jbd2: align h_type and h_line_no in the handle structure on byte boundaries ext4: enable scoped NOFS when starting a handle in nojournal mode ext4: write back partial-zeroed edges in WRITE_ZEROES ext4: zero out whole block for clean edges in WRITE_ZEROES ext4: track partial-zero outcome per edge in ext4_zero_partial_blocks() ext4: clarify return semantics of ext4_load_tail_bh() ext4: move partial block zeroing earlier in ext4_zero_range() ext4: check return value of ext4_get_block() in ext4_load_tail_bh() ext4: skip tail block zeroing for inline data files ...
2026-08-17Merge tag 'fscrypt-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/linuxLinus Torvalds
Pull fscrypt updates from Eric Biggers: "The main change this cycle is a significant simplification that's been overdue for a while now: standardizing on a single file contents encryption implementation in ext4 and f2fs, instead of having two. Specifically, the original filesystem-layer file contents encryption implementation is removed, and the blk-crypto implementation is now used unconditionally. blk-crypto delegates either to inline crypto hardware or to the CPU via blk-crypto-fallback. The latter is functionally equivalent to the original filesystem-layer code. The blk-crypto implementation already existed, but previously it was used only when the filesystem was mounted with "-o inlinecrypt". Now, "-o inlinecrypt" just selects whether inline crypto hardware is used. To allow maintaining that user control over hardware use, the blk-crypto API is extended with a new flag BLK_CRYPTO_CFG_ALLOW_HW. Overall, this removes quite a bit of redundant code from ext4, f2fs, and fs/crypto/. It should make things easier for ongoing filesystem efforts such as iomap support, large folios, and btrfs encryption (btrfs had already been planning to use blk-crypto exclusively.) There are two small behavior changes of note: - Direct I/O now works on encrypted files even without "-o inlinecrypt", rather than falling back to buffered I/O. This is effectively a bugfix, though I'll continue to keep an eye out for any user that may have been depending on the buffered I/O fallback. - IV_INO_LBLK_32 policies are no longer supported in certain cases that didn't make sense and have no known uses. This has been in linux-next since July 22 with no reported issues. All encryption xfstests pass on ext4 and f2fs. As usual I've also been using it on a system with an fscrypt-encrypted home directory. Of course, the blk-crypto code paths also aren't new and were already being used on many systems via the inlinecrypt mount option. In addition to the main change described above, there are a few other cleanups such as using lock guards for mutexes, improving documentation, and removing a workaround for outdated gcc versions" * tag 'fscrypt-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/linux: (29 commits) blk-crypto: Update docs for blk-crypto-fallback motivation blk-crypto: Remove unused function blk_crypto_config_supported() fscrypt: Update docs for data path fscrypt: Remove unused function fscrypt_finalize_bounce_page() f2fs: Update outdated comment in f2fs_write_begin() fs: Update outdated comment for SB_INLINECRYPT fscrypt: Update encryption policy version docs fscrypt: Replace some variable-size memsets with fixed-size fscrypt: Add safety checks to non-block-based en/decryption fscrypt: Merge bio.c and inline_crypt.c into block.c fscrypt: Remove unused functions and workqueue fscrypt: Remove fs-layer zeroout code fscrypt: Remove fscrypt_dio_supported() fscrypt: Replace calls to fscrypt_inode_uses_inline_crypto() fs/buffer: Remove fs-layer decryption code f2fs: Remove fs-layer file contents en/decryption code ext4: Further de-generalize the bio postprocessing code ext4: Make ext4_bio_write_folio() return void ext4: Remove fs-layer file contents en/decryption code Documentation: fscrypt: Update docs for inlinecrypt ...
2026-08-17Merge tag 'vfs-7.3-rc1.sync' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs writeback updates from Christian Brauner: "This makes sync_inode_metadata() and writeback_single_inode() persist not only the inode but all metadata associated with it. A new .sync_inode_metadata superblock operation is called from __writeback_single_inode(). Alongside it a new I_METADATA_WRITEBACK state flag is added. Filesystems no longer need their own mmb_fsync() implementations and can just use simple_fsync(). All metadata is now written for IS_SYNC and IS_DIRSYNC inodes. Races where several fsyncs raced and mmb_sync() could return before all buffers were really persisted are fixed since I_SYNC now serializes properly. The I_METADATA_WRITEBACK scheme also fixes the case where a WB_SYNC_NONE writeback landing between write(2) and fsync(2) left fsync(2) failing to persist the inode. That problem is not specific to filesystems using the generic metadata bh tracking, and the ones that do not are left alone. ext2, udf, bfs, minix, fat and ext4 in nojournal mode have their data integrity writeout fixed and are converted. affs drops metadata bh tracking and mmb_fsync() is removed. A few other fixes came out of this: - a UAF in mark_buffer_write_io_error() - missed inode writeback when racing with __writeback_single_inode() - ext4 allocating the mapping_metadata_bhs struct on demand - three fat fixes: a lost inode update in do_msdos_rename() with DIRSYNC, inode buffer write errors not propagating out of fat_sync_inode_metadata() and directory entries not being persisted on fsync(2) of the root directory" * tag 'vfs-7.3-rc1.sync' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (24 commits) writeback: Export __inode_attach_wb() fat: Fix persisting directory entries on fsync(2) of the root directory fat: Propagate inode buffer write errors from fat_sync_inode_metadata() fat: Fix lost inode update in do_msdos_rename() with DIRSYNC vfs: Remove mmb_fsync() fat: Replace fat_sync_inode() with sync_inode_metadata() fat: Fix missed inode writeback during fsync(2) ext4: Fix data integrity writeout issues in nojournal mode minix: Fix data integrity writeout issues bfs: Fix data integrity writeout issues udf: Fold udf_update_inode() into udf_write_inode() udf: Use sync_inode_metadata() in udf_evict_inode() udf: Drop udf_sync_inode() udf: Use sync_inode_metadata() to writeout IS_SYNC inode udf: Fix data integrity writeout issues ext2: Fix data integrity writeout issues ext2: Avoid unnecessary inode buffer writeback for sync(2) ext2: Drop __ext2_write_inode() ext2: Fix lost inode updates for IS_SYNC inodes fs: Provide way for filesystem to wait for metadata writeback ...
2026-08-06ext4: teach ext4_meta_trans_blocks() about number of allocated extentsJan Kara
So far ext4_meta_trans_blocks() expects that each extent counted in @pextents will be allocated in the transaction we estimate credits for. This is correct for the use in ext4_chunk_trans_blocks() and ext4_chunk_trans_extent() however the use in atomic write path (ext4_convert_unwritten_extents_atomic() and ext4_iomap_alloc() for IOMAP_ATOMIC) unnecessarily overestimates the number of necessary credits as neither of them allocates any data. Add argument to ext4_meta_trans_blocks() for number of extents that are going to be allocated in the transaction. Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260805153605.166545-4-jack@suse.cz Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-31iomap: decouple simple direct I/O reads from iomap_dio_rwChristoph Hellwig
The pending iomap_iter_next conversion creates performance issues for the new simple direct I/O read fast path, because it assumes a model where the iterator must be advanced at the end, which the direct I/O read fast path tries to avoid. Side step this by splitting the simple path from iomap_dio_rw, and require the file systems to call into it explicitly, and pass only a ->begin callback. This allows to drop various checks for incompatible features while creating a requirement for the file system to only call the simple path for cases that it can handle. As a side-benefit we can now inline the initial part of the simple direct I/O read fast path and let the compiler convert the indirect call to ->begin into a direct call. Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Fengnan Chang <changfengnan@bytedance.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260729192737.3190206-4-joannelkoong@gmail.com Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-30ext4: export converted block count from ext4_convert_unwritten_extents()Zhang Yi
ext4_convert_unwritten_extents() currently returns only a success or a failure indication. A zero return means all requested blocks were converted, and a negative value means the conversion failed. However, some blocks may have already been converted when the function fails partway through, and callers have no way to learn how many were done. The WRITE_ZEROES caller in ext4_alloc_file_blocks() needs this information to decide whether to add the inode to the orphan list before updating i_disksize to cover the already-converted written extents, so that a crash before i_disksize catches up can be recovered via orphan truncation. Switch the function to pass out the number of converted blocks through the new output parameter @converted, which will be used by later patches. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260729085918.3336221-2-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-27ext4: Fix data integrity writeout issues in nojournal modeJan Kara
Several racing fsyncs on ext4 in nojournal mode could result in some fsync returning earlier than all metadata buffers were properly persisted. Also ext4_fsync() in nojournal mode was somewhat inefficient because it was always writing out the inode regardless whether it was dirty or not. Fix these issues by using new .sync_inode_metadata method which makes sure all inode related metadata is written to disk during any WB_SYNC_ALL writeback in nojournal mode. This also somewhat simplifies the nojournal mode fsync handling. Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260727104923.3828017-37-jack@suse.cz Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-27ext4: Allocate mapping_metadata_bhs struct on demandJan Kara
Currently every ext4 inode gets mapping_metadata_bhs struct although it is only needed when running without a journal and only for inodes where any metadata was dirtied. Allocate mapping_metadata_bhs struct on demand when dirtying the first metadata buffer for the inode. Acked-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260727104923.3828017-24-jack@suse.cz Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-22ext4: track partial-zero outcome per edge in ext4_zero_partial_blocks()Zhang Yi
Replace the single bool did_zero output of ext4_zero_partial_blocks() with a bitmask that records which edge (start, end, or both in the single-block case) was actually partial-zeroed. This allows callers to distinguish which edges have been zeroed, preparing for unaligned FALLOC_FL_WRITE_ZEROES handling in later patches. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260714080044.4038124-7-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-22ext4: introduce ext4_put_ea_inode() for safe deferred iputYun Zhou
Calling iput() on EA inodes while holding xattr_sem or a jbd2 handle can trigger write_inode_now() -> ext4_writepages() -> s_writepages_rwsem, creating a lock ordering issue during mount (!SB_ACTIVE). Add ext4_put_ea_inode() which uses iput_if_not_last() as a fast path. If this is not the last reference, it is dropped immediately. If this is the last reference, the inode is linked onto a per-sb lock-free llist via i_ea_iput_node (embedded in ext4_inode_info, sharing space with the unused xattr_sem of EA inodes via a union) and a delayed worker (1 jiffie) performs the final iput() in a clean context. This avoids per-iput memory allocation. Flush points ensure all pending EA inode evictions complete before dependent resources become unavailable: - ext4_put_super / failed_mount9: before quota shutdown - failed_mount_wq: before freeing xattr caches - failed_mount3a: before freeing shrinker (journal replay case) - ext4_sync_fs: before remount-ro, freeze, or sync completes Initialization is placed before journal loading since fast commit replay may trigger evictions that call ext4_put_ea_inode(). Also moves init_rwsem(xattr_sem) from init_once to ext4_alloc_inode to handle slab object reuse after the union field has been overwritten. Signed-off-by: Yun Zhou <yun.zhou@windriver.com> Suggested-by: Jan Kara <jack@suse.cz> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260710030851.2791589-3-yun.zhou@windriver.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-22ext4: cleanup unused CONVERT_INLINE_DATA flagAditya Prakash Srivastava
After implementing bitwise flags for tracking the inline data write state in the address space fsdata parameter, the CONVERT_INLINE_DATA state flag is left unused and can be removed. Perform this clean-up by: 1) Deleting the CONVERT_INLINE_DATA definition from ext4.h. 2) Removing the void **fsdata argument from both the forward declaration and the definition of the internal helper ext4_da_convert_inline_data_to_extent(). 3) Removing the void **fsdata argument from the declaration and definition of ext4_generic_write_inline_data() and updating the caller ext4_try_to_write_inline_data() and the internal re-alloc retry logic accordingly. 4) Updating ext4_da_write_begin() to call ext4_generic_write_inline_data() without the fsdata parameter. Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Aditya Prakash Srivastava <aditya.ansh182@gmail.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260703045414.1768-2-aditya.ansh182@gmail.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-22ext4: use fsdata to track inline data write state and fix raceAditya Prakash Srivastava
Instead of checking the live inode state (ext4_has_inline_data(inode) and ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) in the write_end handlers, use the fsdata parameter of the address space operations to explicitly pass down the state in which write_begin prepared the write. A concurrent thread (such as ext4_page_mkwrite()) can convert the inline data to an extent between write_begin and write_end. If this happens, the write_end handlers would previously miss the inline write_end path and fall through to extent-based write_end logic. However, since block buffers were never allocated in write_begin, this resulted in NULL pointer dereferences or data loss because folio_buffers(folio) was NULL. Define EXT4_WRITE_DATA_INLINE (4) as a bit flag (Bit 2), treating fsdata as bitwise flags rather than mutually exclusive enums to keep states of the write path independent. Communicate this state via fsdata: 1) ext4_write_begin() and ext4_da_write_begin() set the EXT4_WRITE_DATA_INLINE bit in *fsdata via bitwise OR when an inline write is successfully prepared. 2) On entry, ext4_write_begin() clears the EXT4_WRITE_DATA_INLINE bit to safely handle VFS retries (where generic_perform_write() bypasses the fsdata initialization on its retry jump). 3) The write_end handlers perform a bitwise AND to check if the EXT4_WRITE_DATA_INLINE bit is set and invoke the inline write_end helper accordingly. Furthermore, during a buffered write, ext4_write_inline_data_end() acquires the xattr lock after preparing the write. If a concurrent page fault (ext4_page_mkwrite()) converts the inline data to an extent after the write_end handlers check the state but before ext4_write_inline_data_end() acquires the xattr write lock, the subsequent check will trigger a kernel panic via BUG_ON(!ext4_has_inline_data(inode)). To keep git history working and bisectability clean, replace the BUG_ON check in ext4_write_inline_data_end() with a graceful error- handling retry path in this same commit. If the inline data is cleared after locking the xattr, we safely release all resources (releasing iloc.bh, unlocking/putting the folio, stopping the active journal transaction handle) and return 0 (VFS retry) to let the generic write path retry the operation safely. Reported-by: syzbot+0c89d865531d053abb2d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0c89d865531d053abb2d Fixes: 3fdcfb668fd7 ("ext4: add journalled write support for inline data") Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Aditya Prakash Srivastava <aditya.ansh182@gmail.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260703045414.1768-1-aditya.ansh182@gmail.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-22ext4: fix ABBA deadlock in ext4_xattr_inode_cache_find()Aditya Prakash Srivastava
Syzbot/stress-ng reported an ABBA deadlock in ext4 when exercising concurrent xattr workloads (using the ea_inode mount/format option). The deadlock occurs between the running transaction and the eviction thread: - Task 1 (stress-ng): Holds a reference to a shared mbcache_entry (ce) and calls ext4_xattr_inode_cache_find() -> ext4_iget() to retrieve the corresponding EA inode. Since the EA inode is currently being evicted, ext4_iget() blocks in __wait_on_freeing_inode() waiting for eviction to complete. - Task 2 (eviction thread): Currently evicting the same EA inode in ext4_evict_ea_inode(). It calls mb_cache_entry_wait_unused(oe) which blocks waiting for Task 1 to release the reference to the mbcache_entry. To break this deadlock, implement a new ext4_iget() configuration flag named EXT4_IGET_NOWAIT. When set, perform a non-blocking lookup of the inode via VFS's find_inode_nowait() API. If the inode is currently being evicted (marked with I_FREEING or I_WILL_FREE) or created (I_CREATING), or if it is not present in the VFS inode cache (cache miss), simply skip it (returning -ENOENT) rather than waiting for eviction/creation to complete, breaking the ABBA cycle. Since we return -ENOENT immediately on a cache miss, we never attempt to allocate a new inode or call iget_locked(), completely eliminating any TOCTOU race window. If the returned inode is I_NEW, wait for its initialization to clear via wait_on_new_inode(). If initialization fails and the inode is unhashed during wait_on_new_inode() waking up (e.g., due to an I/O read error in another thread), safely drop the reference and return -ENOENT. This unhashed check is executed unconditionally on all cache-hit pathways to properly handle concurrent initialization failures. Finally, standard validation checks (including is_bad_inode, EXT4_EA_INODE_FL, file_acl, and xattr flags) are executed as normal inside check_igot_inode() to fully guarantee VFS-layer safety. In ext4_xattr_inode_cache_find(), invoke ext4_iget() with the new EXT4_IGET_NOWAIT flag to perform the non-blocking cache search. Suggested-by: Jan Kara <jack@suse.cz> Reported-by: Colin Ian King <colin.i.king@gmail.com> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219283 Fixes: 0a46ef234756 ("ext4: do not create EA inode under buffer lock") Signed-off-by: Aditya Prakash Srivastava <aditya.ansh182@gmail.com> Tested-by: Colin Ian King <colin.i.king@gmail.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260626054821.1729-1-aditya.ansh182@gmail.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-21ext4: avoid RWM atomic in EXT4_MB_GRP_TEST_AND_SET_READBohdan Trach
EXT4_MB_GRP_TEST_AND_SET_READ uses test_and_set_bit function which issues an atomic write. This can cause high overhead due to cache contention when multiple threads iterate over groups in a tight loop, as is the case for ext4_mb_prefetch(). We have seen this to be a problem for Kunpeng 920b CPUs which uses a single ARM LSE instruction for this purpose. Avoid this unconditional atomic write by testing the bit first without changing its value. This is OK for this use case as this bit is never unset. This change significantly reduces costs of fallocate() operations which trigger linear group scans on large multicore machines where test_and_set_bit issues an atomic write operation unconditionally. Signed-off-by: Bohdan Trach <bohdan.trach@huaweicloud.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260615100331.163997-2-bohdan.trach@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-21ext4: reduce max cluster size to match documented 256MB limitBaokun Li
The mke2fs man page documents: Valid cluster-size values are from 2048 to 256M bytes per cluster. but EXT4_MAX_CLUSTER_LOG_SIZE was set to 30 (1GB), allowing crafted filesystem images to specify cluster sizes up to 1GB. On 32-bit systems with bigalloc enabled, the consistency check in ext4_handle_clustersize(): s_blocks_per_group == s_clusters_per_group * (clustersize / blocksize) can overflow when the cluster ratio is large enough. Since s_blocks_per_group is not range-checked in the bigalloc path, the wrapped product can pass the consistency check, leading to inconsistent group geometry and potential out-of-bounds block allocation. Reduce EXT4_MAX_CLUSTER_LOG_SIZE to 28 to match the documented 256MB limit. With this cap, the maximum product is: (blocksize * 8) * (256M / blocksize) = 2^31 which fits safely in a 32-bit unsigned long for all block sizes. Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://sashiko.dev/#/patchset/20260608061112.392391-1-libaokun%40linux.alibaba.com Signed-off-by: Baokun Li <libaokun@linux.alibaba.com> Reviewed-by: Andreas Dilger <adilger@dilger.ca> Link: https://patch.msgid.link/20260608111150.827117-3-libaokun@linux.alibaba.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-07-20ext4: Further de-generalize the bio postprocessing codeEric Biggers
Since the bio postprocessing code in fs/ext4/readpage.c is now used only for fsverity, rename things accordingly. Also: - Don't create the caches at all when !CONFIG_FS_VERITY. - Remove the unused inode argument from ext4_set_verity_work(). Link: https://patch.msgid.link/20260713023708.9245-10-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org>
2026-07-20ext4: Make ext4_bio_write_folio() return voidEric Biggers
Since the fs-layer file contents encryption implementation was removed, ext4_bio_write_folio() now always returns 0. Change it to return void, and likewise for its caller mpage_submit_folio(). Reviewed-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260713023708.9245-9-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org>
2026-06-18Merge tag 'ext4_for_linus-7.2-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: - A major rework of the fast commit mechanism to avoid lock contention and deadlocks. We also export snapshot statistics in /proc/fs/ext4/*/fc_info - Performance optimization for directory hash computation by processing input in 4-byte chunks and removing function pointers, along with new KUnit tests for directory hash - Cleanups in JBD2 to remove special slabs and use kmalloc() instead - Various bug fixes, including: - Early validation of donor superblock in EXT4_IOC_MOVE_EXT to avoid cross-fs deadlock - Fix for a kernel BUG in ext4_write_inline_data_end under data=journal - Fix for a NULL dereference in jbd2_journal_dirty_metadata when handle is aborted - Fix for an underflow in JBD2 fast commit block initialization check - Fix for LOGFLUSH shutdown ordering to ensure ordered data writeback - Miscellaneous fixes for error path return values and KUnit assertions * tag 'ext4_for_linus-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: validate donor file superblock early in EXT4_IOC_MOVE_EXT ext4: fix kernel BUG in ext4_write_inline_data_end ext4: fix ERR_PTR(0) in ext4_mkdir() jbd2: remove special jbd2 slabs ext4: remove mention of PageWriteback ext4: improve str2hashbuf by processing 4-byte chunks and removing function pointers ext4: add Kunit coverage for directory hash computation ext4: fast commit: export snapshot stats in fc_info ext4: fast commit: add lock_updates tracepoint ext4: fast commit: avoid i_data_sem by dropping ext4_map_blocks() in snapshots ext4: fast commit: avoid self-deadlock in inode snapshotting ext4: fast commit: avoid waiting for FC_COMMITTING ext4: lockdep: handle i_data_sem subclassing for special inodes ext4: fast commit: snapshot inode state before writing log jbd2: fix integer underflow in jbd2_journal_initialize_fast_commit() ext4: fix fast commit wait/wake bit mapping on 64-bit jbd2: check for aborted handle in jbd2_journal_dirty_metadata() ext4: Use %pe to print PTR_ERR() ext4: fix LOGFLUSH shutdown ordering to allow ordered-mode data writeback ext4: replace KUnit tests for memcmp() with KUNIT_ASSERT_MEMEQ()
2026-06-04ext4; Convert __ext4_read_bh() to bh_submit()Matthew Wilcox (Oracle)
Avoid an extra indirect function call and changing the buffer refcount by converting ext4_end_bitmap_read() from bh_end_io_t to bio_end_io_t and calling bh_submit(). Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org> Link: https://patch.msgid.link/20260528173150.1093780-13-willy@infradead.org Reviewed-by: Jan Kara <jack@suse.cz> Cc: linux-ext4@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-06-03ext4: fast commit: export snapshot stats in fc_infoLi Chen
Snapshot-based fast commit can fall back when the commit-time snapshot cannot be built (e.g. extent status cache misses). It is useful to quantify the updates-locked window and to see why snapshotting failed. Add best-effort snapshot counters to the ext4 superblock and extend /proc/fs/ext4/<sb_id>/fc_info to report the number of snapshotted inodes and ranges, snapshot failure reasons, and the average/max time spent with journal updates locked. Signed-off-by: Li Chen <chenl311@chinatelecom.cn> Link: https://patch.msgid.link/20260515091829.194810-8-me@linux.beauty Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-06-03ext4: fast commit: add lock_updates tracepointLi Chen
Commit-time fast commit snapshots run under jbd2_journal_lock_updates(), so it is useful to quantify the time spent with updates locked and to understand why snapshotting can fail. Add a new tracepoint, ext4_fc_lock_updates, reporting the time spent in the updates-locked window along with the number of snapshotted inodes and ranges. Record the first snapshot failure reason in a stable snap_err field for tooling. Signed-off-by: Li Chen <chenl311@chinatelecom.cn> Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org> Link: https://patch.msgid.link/20260515091829.194810-7-me@linux.beauty Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-06-03ext4: fast commit: avoid waiting for FC_COMMITTINGLi Chen
ext4_fc_track_inode() can be called while holding i_data_sem (e.g. fallocate). Waiting for EXT4_STATE_FC_COMMITTING in that case risks an ABBA deadlock: i_data_sem -> wait(FC_COMMITTING) vs FC_COMMITTING -> wait(i_data_sem) in the commit task. Now that fast commit snapshots inode state at commit time, updates during log writing do not need to block. Drop the wait and lockdep assertion in ext4_fc_track_inode(), and make ext4_fc_del() wait for FC_COMMITTING so an inode cannot be removed while the commit thread is still using it. When an inode is modified during a fast commit, mark it with EXT4_STATE_FC_REQUEUE so cleanup keeps it queued for the next fast commit. This is needed because jbd2_fc_end_commit() invokes the cleanup callback with tid == 0, so tid-based requeue logic would requeue every inode. Testing: tracepoint ext4:ext4_fc_commit_stop with two fsyncs in the same transaction. nblks is the number of journal blocks written for that fast commit. Before this change, the second fsync still wrote almost the same fast commit log (nblks 10->9), because tid == 0 in jbd2_fc_end_commit() caused the tid-based requeue logic to keep all inodes queued. After this change, only inodes modified during the commit are requeued, and the second fsync wrote a nearly empty fast commit (nblks 10->1). Signed-off-by: Li Chen <chenl311@chinatelecom.cn> Link: https://patch.msgid.link/20260515091829.194810-4-me@linux.beauty Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-06-03ext4: lockdep: handle i_data_sem subclassing for special inodesLi Chen
Fast commit can hold s_fc_lock while writing journal blocks. Mapping the journal inode can take its i_data_sem. Normal inode update paths can take a data inode i_data_sem and then s_fc_lock, which makes lockdep report a circular dependency. lockdep treats all i_data_sem instances as one lock class and cannot distinguish the journal inode i_data_sem from a regular inode i_data_sem. The journal inode is not tracked by fast commit and no FC waiters ever depend on it, so this is not a real ABBA deadlock. Assign the journal inode a dedicated i_data_sem lockdep subclass to avoid the false positive. Inode cache objects can be recycled, so also reset i_data_sem to I_DATA_SEM_NORMAL when allocating an ext4 inode. Otherwise a new inode may inherit an old subclass (journal/quota/ea) and trigger lockdep warnings. Signed-off-by: Li Chen <chenl311@chinatelecom.cn> Link: https://patch.msgid.link/20260515091829.194810-3-me@linux.beauty Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-06-03ext4: fast commit: snapshot inode state before writing logLi Chen
Fast commit writes inode metadata and data range updates after unlocking journal updates. New handles can start at that point, so the log writing path must not look at live inode state. Add a commit-time per-inode snapshot and populate it while journal updates are locked and existing handles are drained. Store the snapshot behind ext4_inode_info->i_fc_snap so ext4_inode_info only grows by one pointer. The snapshot contains a copy of the on-disk inode plus the data range records needed for fast commit TLVs. Snapshotting runs under jbd2_journal_lock_updates(). Avoid triggering I/O there by using ext4_get_inode_loc_noio() and falling back to full commit if the inode table block is not present or not uptodate. Log writing then only serializes the snapshot, so it no longer needs to call ext4_map_blocks() and take i_data_sem under s_fc_lock. The snapshot is installed and freed under s_fc_lock and is released from fast commit cleanup and inode eviction. Signed-off-by: Li Chen <chenl311@chinatelecom.cn> Link: https://patch.msgid.link/20260515091829.194810-2-me@linux.beauty Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-06-03ext4: fix fast commit wait/wake bit mapping on 64-bitLi Chen
On 64-bit, ext4 dynamic inode states live in the upper half of i_flags, and ext4_test_inode_state() applies the corresponding +32 offset. The fast-commit wait and wake paths open-coded the wait key with the raw EXT4_STATE_* value. Add small helpers for the state wait word and bit, and use them for the FC_COMMITTING and FC_FLUSHING_DATA waits so the wait key follows the same mapping as the state helpers. Fixes: 857d32f26181 ("ext4: rework fast commit commit path") Reported-by: Sashiko AI review <sashiko-bot@kernel.org> Signed-off-by: Li Chen <chenl311@chinatelecom.cn> Reviewed-by: Baokun Li <libaokun@linux.alibaba.com> Reviewed-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260513085818.552432-1-me@linux.beauty Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-04-17Merge tag 'ext4_for_linux-7.0-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: - Refactor code paths involved with partial block zero-out in prearation for converting ext4 to use iomap for buffered writes - Remove use of d_alloc() from ext4 in preparation for the deprecation of this interface - Replace some J_ASSERTS with a journal abort so we can avoid a kernel panic for a localized file system error - Simplify various code paths in mballoc, move_extent, and fast commit - Fix rare deadlock in jbd2_journal_cancel_revoke() that can be triggered by generic/013 when blocksize < pagesize - Fix memory leak when releasing an extended attribute when its value is stored in an ea_inode - Fix various potential kunit test bugs in fs/ext4/extents.c - Fix potential out-of-bounds access in check_xattr() with a corrupted file system - Make the jbd2_inode dirty range tracking safe for lockless reads - Avoid a WARN_ON when writeback files due to a corrupted file system; we already print an ext4 warning indicatign that data will be lost, so the WARN_ON is not necessary and doesn't add any new information * tag 'ext4_for_linux-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (37 commits) jbd2: fix deadlock in jbd2_journal_cancel_revoke() ext4: fix missing brelse() in ext4_xattr_inode_dec_ref_all() ext4: fix possible null-ptr-deref in mbt_kunit_exit() ext4: fix possible null-ptr-deref in extents_kunit_exit() ext4: fix the error handling process in extents_kunit_init). ext4: call deactivate_super() in extents_kunit_exit() ext4: fix miss unlock 'sb->s_umount' in extents_kunit_init() ext4: fix bounds check in check_xattrs() to prevent out-of-bounds access ext4: zero post-EOF partial block before appending write ext4: move pagecache_isize_extended() out of active handle ext4: remove ctime/mtime update from ext4_alloc_file_blocks() ext4: unify SYNC mode checks in fallocate paths ext4: ensure zeroed partial blocks are persisted in SYNC mode ext4: move zero partial block range functions out of active handle ext4: pass allocate range as loff_t to ext4_alloc_file_blocks() ext4: remove handle parameters from zero partial block functions ext4: move ordered data handling out of ext4_block_do_zero_range() ext4: rename ext4_block_zero_page_range() to ext4_block_zero_range() ext4: factor out journalled block zeroing range ext4: rename and extend ext4_block_truncate_page() ...
2026-04-13Merge tag 'vfs-7.1-rc1.bh.metadata' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs buffer_head updates from Christian Brauner: "This cleans up the mess that has accumulated over the years in metadata buffer_head tracking for inodes. It moves the tracking into dedicated structure in filesystem-private part of the inode (so that we don't use private_list, private_data, and private_lock in struct address_space), and also moves couple other users of private_data and private_list so these are removed from struct address_space saving 3 longs in struct inode for 99% of inodes" * tag 'vfs-7.1-rc1.bh.metadata' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (42 commits) fs: Drop i_private_list from address_space fs: Drop mapping_metadata_bhs from address space ext4: Track metadata bhs in fs-private inode part minix: Track metadata bhs in fs-private inode part udf: Track metadata bhs in fs-private inode part fat: Track metadata bhs in fs-private inode part bfs: Track metadata bhs in fs-private inode part affs: Track metadata bhs in fs-private inode part ext2: Track metadata bhs in fs-private inode part fs: Provide functions for handling mapping_metadata_bhs directly fs: Switch inode_has_buffers() to take mapping_metadata_bhs fs: Make bhs point to mapping_metadata_bhs fs: Move metadata bhs tracking to a separate struct fs: Fold fsync_buffers_list() into sync_mapping_buffers() fs: Drop osync_buffers_list() kvm: Use private inode list instead of i_private_list fs: Remove i_private_data aio: Stop using i_private_data and i_private_lock hugetlbfs: Stop using i_private_data fs: Stop using i_private_data for metadata bh tracking ...
2026-04-13Merge tag 'vfs-7.1-rc1.kino' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs i_ino updates from Christian Brauner: "For historical reasons, the inode->i_ino field is an unsigned long, which means that it's 32 bits on 32 bit architectures. This has caused a number of filesystems to implement hacks to hash a 64-bit identifier into a 32-bit field, and deprives us of a universal identifier field for an inode. This changes the inode->i_ino field from an unsigned long to a u64. This shouldn't make any material difference on 64-bit hosts, but 32-bit hosts will see struct inode grow by at least 4 bytes. This could have effects on slabcache sizes and field alignment. The bulk of the changes are to format strings and tracepoints, since the kernel itself doesn't care that much about the i_ino field. The first patch changes some vfs function arguments, so check that one out carefully. With this change, we may be able to shrink some inode structures. For instance, struct nfs_inode has a fileid field that holds the 64-bit inode number. With this set of changes, that field could be eliminated. I'd rather leave that sort of cleanups for later just to keep this simple" * tag 'vfs-7.1-rc1.kino' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: nilfs2: fix 64-bit division operations in nilfs_bmap_find_target_in_group() EVM: add comment describing why ino field is still unsigned long vfs: remove externs from fs.h on functions modified by i_ino widening treewide: fix missed i_ino format specifier conversions ext4: fix signed format specifier in ext4_load_inode trace event treewide: change inode->i_ino from unsigned long to u64 nilfs2: widen trace event i_ino fields to u64 f2fs: widen trace event i_ino fields to u64 ext4: widen trace event i_ino fields to u64 zonefs: widen trace event i_ino fields to u64 hugetlbfs: widen trace event i_ino fields to u64 ext2: widen trace event i_ino fields to u64 cachefiles: widen trace event i_ino fields to u64 vfs: widen trace event i_ino fields to u64 net: change sock.sk_ino and sock_i_ino() to u64 audit: widen ino fields to u64 vfs: widen inode hash/lookup functions to u64
2026-04-09ext4: ensure zeroed partial blocks are persisted in SYNC modeZhang Yi
In ext4_zero_range() and ext4_punch_hole(), when operating in SYNC mode and zeroing a partial block, only data=journal modes guarantee that the zeroed data is synchronously persisted after the operation completes. For data=ordered/writeback mode and non-journal modes, this guarantee is missing. Introduce a partial_zero parameter to explicitly trigger writeback for all scenarios where a partial block is zeroed, ensuring the zeroed data is durably persisted. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Link: https://patch.msgid.link/20260327102939.1095257-10-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-04-09ext4: remove handle parameters from zero partial block functionsZhang Yi
Only journal data mode requires an active journal handle when zeroing partial blocks. Stop passing handle_t *handle to ext4_zero_partial_blocks() and related functions, and make ext4_block_journalled_zero_range() start a handle independently. This change has no practical impact now because all callers invoke these functions within the context of an active handle. It prepares for moving ext4_block_zero_eof() out of an active handle in the next patch, which is a prerequisite for converting block zero range operations to iomap infrastructure. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260327102939.1095257-7-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-04-09ext4: rename and extend ext4_block_truncate_page()Zhang Yi
Rename ext4_block_truncate_page() to ext4_block_zero_eof() and extend its signature to accept an explicit 'end' offset instead of calculating the block boundary. This helper function now can replace all cases requiring zeroing of the partial EOF block, including the append buffered write paths in ext4_*_write_end(). Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260327102939.1095257-3-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2026-04-09ext4: move dcache manipulation out of __ext4_link()NeilBrown
__ext4_link() has two callers. - ext4_link() calls it during normal handling of the link() system call or similar - ext4_fc_replay_link_internal() calls it when replaying the journal at mount time. The former needs changes to dcache - instantiating the dentry to the inode on success. The latter doesn't need or want any dcache manipulation. So move the manipulation out of __ext4_link() and do it in ext4_link() only. This requires: - passing the qname from the dentry explicitly to __ext4_link. The parent dir is already passed. The dentry is still passed in the ext4_link() case purely for use by ext4_fc_track_link(). - passing the inode separately to ext4_fc_track_link() as the dentry will not be instantiated yet. - using __ext4_add_entry() in ext4_link, which doesn't need a dentry. - moving ihold(), d_instantiate(), drop_nlink() and iput() calls out of __ext4_link() into ext4_link(). Note that ext4_inc_count() and drop_nlink() remain in __ext4_link() as both callers need them and they are not related to the dentry. This substantially simplifies ext4_fc_replay_link_internal(), and removes a use of d_alloc() which, it is planned, will be removed. Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20260320000838.3797494-4-neilb@ownmail.net Signed-off-by: Theodore Ts'o <tytso@mit.edu>