Linux EXT4 FS development
 help / color / mirror / Atom feed
* [PATCH RFC 0/9] fs: reclaim inodes with I_METADATA_WRITEBACK
@ 2026-10-29 13:28 Christian Brauner
  2026-10-29 13:28 ` [PATCH RFC 1/9] ext2: add ext2_inode_block() helper Christian Brauner
                   ` (8 more replies)
  0 siblings, 9 replies; 19+ messages in thread
From: Christian Brauner @ 2026-10-29 13:28 UTC (permalink / raw)
  To: linux-fsdevel, Jan Kara
  Cc: Josef Bacik, Chris Mason, linux-ext4, OGAWA Hirofumi,
	Andreas Dilger, Baokun Li, Ojaswin Mujoo, Ritesh Harjani (IBM),
	Namjae Jeon, Christian Brauner (Amutable), stable

Since v7.3 ext2, ext4 without a journal, fat, udf and minix set
I_METADATA_WRITEBACK when they copy an inode into its buffer and only
fsync() and friends clear it again.

inode_lru_isolate() treats it like any other state bit and takes the
inode off the LRU, expecting something to put it back once the bit is
gone but nothing ever puts it back.

So these inodes can't be reclaimed anymore. They stay around until
something fsyncs them or the filesystem is unmounted. Which is
effectively a DoS.

On ext4 without a journal an atime update is enough to pin an inode, and
creating 60000 small files in a memory cgroup limited to 96M gets the
task OOM killed after about 43000 of them.

The original provided fix was quite ugly. It handed such inodes from
inode_lru_isolate() to a per-sb work item with a reference held, synced
them there and put them back on the LRU.

We've had discussions in that direction for unrelated reasons and we
found that quite terrible. It's a second inode reclaim offload mechanism
next to Jan's deferred inode reclaim, it needs a new inode state bit,
and the reference shows up in everything that looks for open deleted
files.

So instead, let the shrinker reclaim these inodes like any other clean
inode and make evict() write out what I_METADATA_WRITEBACK tracks
unless the inode is being deleted. That's what the filesystems already
do for the metadata buffers they track in ->evict_inode().

The first seven patches make ->sync_inode_metadata() of ext2, ext4,
minix and udf look the inode block up in the buffer cache instead of
reading it from disk, the way fat already does. A block that isn't
cached is neither dirty nor under writeback so there's nothing to write
or to wait for. That matters once evict() calls it from reclaim.

The eighth patch is a fat fix in the same area. fat_evict_inode()
updates the directory entry of a file with fallocated clusters and lets
the inode go without writing it out. So the freed clusters can be free
on disk while the directory entry still points at them.

The last patch is the actual fix.

This still writes and waits from reclaim if the inode block is still
dirty. Once Jan's deferred inode reclaim lands these inodes can go to
his workers instead. I have that on top of his v3 and will send it as
an RFC in a minute.

All of this is meant for v7.3 stable. v7.3 still has bfs which sets
I_METADATA_WRITEBACK as well, so the backport also needs a cache-only
lookup in bfs_sync_inode_metadata().

Tested on 65ba8f34ec88 with KASAN and lockdep: the reproducer in the
last patch leaves about 5000 of 5000 inodes around on ext2, ext4, vfat
and minix without this series and at most one with it. The memory
cgroup run creates all 60000 files and nothing gets killed. The
reproducer in the fat patch finds /a pointing at a free cluster on disk
without the series and a clean /a with it. No splats.

udf inodes with their data in the ICB are still dirty after sync(2),
with and without this series. That's a separate issue I didn't care
about.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (9):
      ext2: add ext2_inode_block() helper
      ext2: don't read the inode block in ext2_sync_inode_metadata()
      ext4: add ext4_inode_loc_block() helper
      ext4: don't read the inode block in ext4_sync_inode_metadata()
      minix: add minix_inode_block() helper
      minix: don't read the inode block in minix_sync_inode_metadata()
      udf: don't allocate the inode block in udf_sync_inode_metadata()
      fat: sync the inode in fat_free_eofblocks()
      fs: don't strand I_METADATA_WRITEBACK inodes outside the LRU

 fs/ext2/inode.c   | 61 ++++++++++++++++++++++++++++++++++---------------------
 fs/ext4/inode.c   | 48 ++++++++++++++++++++++++++++++-------------
 fs/fat/inode.c    |  2 +-
 fs/inode.c        | 13 +++++++++++-
 fs/minix/bitmap.c | 37 ++++++++++++++++++++-------------
 fs/minix/inode.c  | 16 +++++++--------
 fs/minix/minix.h  |  1 +
 fs/udf/inode.c    | 27 ++++++++++++------------
 8 files changed, 130 insertions(+), 75 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261029-work-inode-metadata_writeback-d880a0429e49


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-10-29 13:46 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 13:28 [PATCH RFC 0/9] fs: reclaim inodes with I_METADATA_WRITEBACK Christian Brauner
2026-10-29 13:28 ` [PATCH RFC 1/9] ext2: add ext2_inode_block() helper Christian Brauner
2026-10-29 13:31   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 2/9] ext2: don't read the inode block in ext2_sync_inode_metadata() Christian Brauner
2026-10-29 13:33   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 3/9] ext4: add ext4_inode_loc_block() helper Christian Brauner
2026-10-29 13:32   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 4/9] ext4: don't read the inode block in ext4_sync_inode_metadata() Christian Brauner
2026-10-29 13:34   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 5/9] minix: add minix_inode_block() helper Christian Brauner
2026-10-29 13:34   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 6/9] minix: don't read the inode block in minix_sync_inode_metadata() Christian Brauner
2026-10-29 13:32   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 7/9] udf: don't allocate the inode block in udf_sync_inode_metadata() Christian Brauner
2026-10-29 13:35   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 8/9] fat: sync the inode in fat_free_eofblocks() Christian Brauner
2026-10-29 13:37   ` sashiko-bot
2026-10-29 13:28 ` [PATCH RFC 9/9] fs: don't strand I_METADATA_WRITEBACK inodes outside the LRU Christian Brauner
2026-10-29 13:46   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox