From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org, Jan Kara <jack@suse.cz>
Cc: Josef Bacik <josef@toxicpanda.com>,
Chris Mason <mason@kernel.org>,
linux-ext4@vger.kernel.org,
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>,
Andreas Dilger <adilger.kernel@dilger.ca>,
Baokun Li <libaokun@linux.alibaba.com>,
Ojaswin Mujoo <ojaswin@linux.ibm.com>,
"Ritesh Harjani (IBM)" <ritesh.list@gmail.com>,
Namjae Jeon <namjae.jeon@samsung.com>,
"Christian Brauner (Amutable)" <brauner@kernel.org>,
stable@vger.kernel.org
Subject: [PATCH RFC v3 00/14] fs: reclaim inodes with I_METADATA_WRITEBACK
Date: Mon, 09 Nov 2026 17:51:15 +0100 [thread overview]
Message-ID: <20261109-work-inode-metadata_writeback-v3-0-91ac6c024018@kernel.org> (raw)
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. Nothing ever does. So these inodes can't be reclaimed until
something fsyncs them or the filesystem is unmounted. That's 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.
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, so that calling it from
reclaim is fine. The eighth sets I_METADATA_WRITEBACK from
mmb_mark_buffer_dirty() so that the bit covers the tracked metadata
buffers as well. The ninth is the actual fix. The last five drop the
mmb_sync() calls from ->evict_inode() that evict() makes redundant.
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 (cf. [1]).
All of this is meant for v7.3 stable. v7.3 still has bfs which sets
I_METADATA_WRITEBACK as well, so the backport needs the cache-only
lookup for bfs too.
Tested on 65ba8f34ec88 with KASAN and lockdep: creating 5000 files,
sync(2) and two drop_caches leave about 5000 of 5000 inodes around on
ext2, ext4, vfat and minix without this series and none with it. The
memory cgroup run creates all 60000 files and nothing gets killed.
Blocks the flusher left dirty get written out on eviction on all five
filesystems. 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.
[1]: https://patch.msgid.link/20261029-work-inode-metadata_writeback-deferred-v1-1-7e49f6c12b60@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Changes in v3:
- Set I_METADATA_WRITEBACK from mmb_mark_buffer_dirty() and write out
what it tracks from evict() again instead of from every
->evict_inode() (Jan).
- Drop the mmb_sync() calls from the ->evict_inode() of ext2, ext4,
minix, udf and fat.
- Jan's Reviewed-by on the fix isn't carried over since the patch
changed.
- Link to v2: https://patch.msgid.link/20261105-work-inode-metadata_writeback-v2-0-48628c60c336@kernel.org
Changes in v2:
- Drop the ext4_inode_loc_block() helper and use
ext4_get_inode_loc_noio() with the sleeping buffer lookup instead
(Jan).
- Write out what I_METADATA_WRITEBACK tracks from ->evict_inode() of
each filesystem instead of from evict() (Jan).
- Drop "fat: sync the inode in fat_free_eofblocks()" (Jan, OGAWA).
- Move the reproducer out of the commit message.
- Link to v1: https://patch.msgid.link/20261029-work-inode-metadata_writeback-v1-0-7d6c678a585d@kernel.org
---
Christian Brauner (14):
ext2: add ext2_inode_block() helper
ext2: don't read the inode block in ext2_sync_inode_metadata()
ext4: use sb_find_get_block_nonatomic() in ext4_get_inode_loc_noio()
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()
fs: set I_METADATA_WRITEBACK in mmb_mark_buffer_dirty()
fs: don't strand I_METADATA_WRITEBACK inodes outside the LRU
ext2: drop mmb_sync() from ext2_evict_inode()
ext4: drop mmb_sync() from ext4_evict_inode()
minix: drop mmb_sync() from minix_evict_inode()
udf: drop mmb_sync() from udf_evict_inode()
fat: drop mmb_sync() from fat_evict_inode()
fs/buffer.c | 16 ++++++++++++--
fs/ext2/inode.c | 63 +++++++++++++++++++++++++++++++++----------------------
fs/ext4/inode.c | 22 +++++++++----------
fs/fat/inode.c | 1 -
fs/inode.c | 13 +++++++++++-
fs/minix/bitmap.c | 37 +++++++++++++++++++-------------
fs/minix/inode.c | 18 +++++++---------
fs/minix/minix.h | 1 +
fs/udf/inode.c | 33 +++++++++++++----------------
9 files changed, 121 insertions(+), 83 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261029-work-inode-metadata_writeback-d880a0429e49
next reply other threads:[~2026-11-09 16:51 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 16:51 Christian Brauner [this message]
2026-11-09 16:51 ` [PATCH RFC v3 01/14] ext2: add ext2_inode_block() helper Christian Brauner
2026-11-09 16:57 ` sashiko-bot
2026-11-09 16:51 ` [PATCH RFC v3 02/14] ext2: don't read the inode block in ext2_sync_inode_metadata() Christian Brauner
2026-11-09 17:02 ` sashiko-bot
2026-11-09 16:51 ` [PATCH RFC v3 03/14] ext4: use sb_find_get_block_nonatomic() in ext4_get_inode_loc_noio() Christian Brauner
2026-11-09 17:06 ` sashiko-bot
2026-11-09 16:51 ` [PATCH RFC v3 04/14] ext4: don't read the inode block in ext4_sync_inode_metadata() Christian Brauner
2026-11-09 17:11 ` sashiko-bot
2026-11-09 16:51 ` [PATCH RFC v3 05/14] minix: add minix_inode_block() helper Christian Brauner
2026-11-09 17:13 ` sashiko-bot
2026-11-09 16:51 ` [PATCH RFC v3 06/14] minix: don't read the inode block in minix_sync_inode_metadata() Christian Brauner
2026-11-09 17:16 ` sashiko-bot
2026-11-09 16:51 ` [PATCH RFC v3 07/14] udf: don't allocate the inode block in udf_sync_inode_metadata() Christian Brauner
2026-11-09 17:24 ` sashiko-bot
2026-11-09 16:51 ` [PATCH RFC v3 08/14] fs: set I_METADATA_WRITEBACK in mmb_mark_buffer_dirty() Christian Brauner
2026-11-09 17:50 ` sashiko-bot
2026-11-10 10:33 ` Jan Kara
2026-11-09 16:51 ` [PATCH RFC v3 09/14] fs: don't strand I_METADATA_WRITEBACK inodes outside the LRU Christian Brauner
2026-11-09 18:18 ` sashiko-bot
2026-11-10 10:28 ` Jan Kara
2026-11-09 16:51 ` [PATCH RFC v3 10/14] ext2: drop mmb_sync() from ext2_evict_inode() Christian Brauner
2026-11-09 18:36 ` sashiko-bot
2026-11-10 10:28 ` Jan Kara
2026-11-09 16:51 ` [PATCH RFC v3 11/14] ext4: drop mmb_sync() from ext4_evict_inode() Christian Brauner
2026-11-09 18:55 ` sashiko-bot
2026-11-10 10:28 ` Jan Kara
2026-11-09 16:51 ` [PATCH RFC v3 12/14] minix: drop mmb_sync() from minix_evict_inode() Christian Brauner
2026-11-09 18:58 ` sashiko-bot
2026-11-10 10:28 ` Jan Kara
2026-11-09 16:51 ` [PATCH RFC v3 13/14] udf: drop mmb_sync() from udf_evict_inode() Christian Brauner
2026-11-09 19:03 ` sashiko-bot
2026-11-10 10:29 ` Jan Kara
2026-11-09 16:51 ` [PATCH RFC v3 14/14] fat: drop mmb_sync() from fat_evict_inode() Christian Brauner
2026-11-09 19:08 ` sashiko-bot
2026-11-10 10:29 ` Jan Kara
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109-work-inode-metadata_writeback-v3-0-91ac6c024018@kernel.org \
--to=brauner@kernel.org \
--cc=adilger.kernel@dilger.ca \
--cc=hirofumi@mail.parknet.co.jp \
--cc=jack@suse.cz \
--cc=josef@toxicpanda.com \
--cc=libaokun@linux.alibaba.com \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=mason@kernel.org \
--cc=namjae.jeon@samsung.com \
--cc=ojaswin@linux.ibm.com \
--cc=ritesh.list@gmail.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox