evict() writes out the metadata that I_METADATA_WRITEBACK tracks for an inode that isn't being deleted. When the inode shrinker evicts such an inode that I/O happens from kswapd or direct reclaim. Keeping I/O out of reclaim is what deferred inode reclaim is for. So hand these inodes to the deferred reclaim workers. Don't make the filesystems mark them with I_DEFER_RECLAIM though. On ext4 without a journal the bit is set by nearly every inode modification, atime updates included, and the mark can't be cleared again. So nearly every inode that was ever touched would end up being deferred. Instead, decide when the inode is isolated. Once a data integrity writeback has cleared I_METADATA_WRITEBACK the inode is evicted directly again. Signed-off-by: Christian Brauner (Amutable) --- This goes on top of Jan's deferred inode reclaim v3 (cf. [1]) and my I_METADATA_WRITEBACK reclaim fixes (cf. [2]). It's an RFC because neither of them has landed yet. Jan, feel free to just fold it into your series if you like it. [2] makes evict() write out what I_METADATA_WRITEBACK tracks. When the inode shrinker evicts such an inode that write happens from kswapd or from direct reclaim, which is what deferred inode reclaim is meant to avoid. I didn't use mark_inode_reclaim_deferred() for this. ext4 without a journal sets the bit on nearly every inode modification, atime updates included, and the mark is permanent. So nearly every inode that was ever touched would end up being deferred. Looking at the bit when the inode is isolated defers only the inodes that still need it. That's the case Andreas asked about (cf. [3]). Tested with the reproducer from [2] on top of v3: same numbers as with [2] alone, no OOM in the memory cgroup run, no splats. [1]: https://lore.kernel.org/r/20261009133253.20102-1-jack@suse.cz [2]: https://patch.msgid.link/20261029-work-inode-metadata_writeback-v1-0-7d6c678a585d@kernel.org [3]: https://lore.kernel.org/r/CAHc6FU4Rf9-r8m8SSb=e0k0zyoFhQAdjupHMWvT6rStBshntug@mail.gmail.com --- fs/inode.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/inode.c b/fs/inode.c index c67ec6aa490b..9420afaaf7ce 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1086,7 +1086,12 @@ long prune_icache_sb(struct super_block *sb, struct shrink_control *sc) inode_lru_isolate, &freeable); list_for_each_entry_safe(inode, tmp, &freeable, i_lru) { - if (inode_state_read_once(inode) & I_DEFER_RECLAIM) { + /* + * evict() writes out what I_METADATA_WRITEBACK tracks, don't + * do that from reclaim. + */ + if (inode_state_read_once(inode) & + (I_DEFER_RECLAIM | I_METADATA_WRITEBACK)) { deferred++; continue; } --- base-commit: 548be8616e3e38a9dfc02bd766582ec0acf06647 change-id: 20261029-work-inode-metadata_writeback-deferred-94f716db429a