From: Julian Sun <sunjunchao@bytedance.com>
To: Jan Kara <jack@suse.cz>
Cc: linux-block@vger.kernel.org, linux-fsdevel@vger.kernel.org,
gfs2@lists.linux.dev, linux-security-module@vger.kernel.org,
agruenba@redhat.com, mic@digikod.net, gnoack@google.com,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
aleksa@amutable.com, legion@kernel.org, djwong@kernel.org,
ebiggers@kernel.org, sandeen@redhat.com
Subject: Re: [PATCH 2/7] fs: introduce sb_for_each_inodes().
Date: Fri, 11 Sep 2026 11:35:23 +0800 [thread overview]
Message-ID: <1891f4ac-139e-4e9c-80ff-802ec2bb2844@bytedance.com> (raw)
In-Reply-To: <yhfoyto6skszguj447a7ifajl2r6qiwj5japcg2zsj2ooshi4r@tgkq5fadmyto>
On 9/11/26 1:47 AM, Jan Kara wrote:
> On Wed 09-09-26 17:01:07, Julian Sun wrote:
>> Add sb_for_each_inodes() to share s_inodes traversal and preserve its
>> position while s_inode_list_lock is dropped.
>>
>> Track active iterators on sb->s_inodes_iters and advance their saved
>> positions before unlinking an inode. Callbacks manage inode references
>> and per-inode work, allowing both normal walks and eviction to use the
>> same interface.
>>
>> Signed-off-by: Julian Sun <sunjunchao@bytedance.com>
>
> So I'm not generally opposed the to a superblock inode iterator idea but
> what you have looks more complex than I'd expect. Also we shouldn't tie
> that to the fix of the lockup in evict_inodes(). So first I'd just
> concentrate on fixing that lockup, then we can have a look at the iterator
> idea.
>
> You can take some inspiration about inode iteration API from the Dave
> Chinner's patch set [1]. That looked more like what I'd expect although
> Christoph had some comments on it too.
>
> Regarding the fix I'd just make evict_inodes() do what all other inode
> iterators do when they decide to drop s_inode_list_lock - pin an inode in
> the list by grabbing refcount and then resume from it.
Thanks for the feedback. I agree that we should fix the lockup separately,
and I'll focus on that first.
I had seen Dave Chinner's series before. I'll revisit it and the review
comments when working on the inode iteration API.>
> Honza
>
> [1] https://lore.kernel.org/all/20241002014017.3801899-1-david@fromorbit.com/
>
>> ---
>> fs/inode.c | 93 ++++++++++++++++++++++++++++++++++
>> fs/super.c | 1 +
>> include/linux/fs.h | 15 ++++++
>> include/linux/fs/super_types.h | 3 +-
>> 4 files changed, 111 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/inode.c b/fs/inode.c
>> index ba7da39be4a3..b4279063a5dd 100644
>> --- a/fs/inode.c
>> +++ b/fs/inode.c
>> @@ -69,6 +69,15 @@ const struct address_space_operations empty_aops = {
>> };
>> EXPORT_SYMBOL(empty_aops);
>>
>> +struct inode_iter {
>> + struct list_head iters_node; /* sb->s_inodes_iters */
>> + struct list_head *next; /* next node going to iterate */
>> + unsigned int flags;
>> + inode_iter_cb func;
>> + void *data;
>> + int ret;
>> +};
>> +
>> static DEFINE_PER_CPU(unsigned long, nr_inodes);
>> static DEFINE_PER_CPU(unsigned long, nr_unused);
>>
>> @@ -641,12 +650,96 @@ void inode_sb_list_add(struct inode *inode)
>> }
>> EXPORT_SYMBOL_GPL(inode_sb_list_add);
>>
>> +static void inode_sb_iter_start(struct super_block *sb, struct inode_iter *it,
>> + unsigned int flags, inode_iter_cb fn, void *data)
>> +{
>> + it->flags = flags;
>> + it->func = fn;
>> + it->data = data;
>> + it->ret = 0;
>> + spin_lock(&sb->s_inode_list_lock);
>> + it->next = sb->s_inodes.next;
>> + list_add(&it->iters_node, &sb->s_inodes_iters);
>> +}
>> +
>> +static void inode_sb_iter_end(struct inode_iter *it, struct super_block *sb)
>> +{
>> + list_del(&it->iters_node);
>> + spin_unlock(&sb->s_inode_list_lock);
>> +}
>> +
>> +static bool inode_sb_iter_next(struct inode_iter *it, struct super_block *sb)
>> +{
>> + struct inode *inode = NULL;
>> + int ret;
>> +
>> + while (!inode && it->next != &sb->s_inodes) {
>> + inode = list_entry(it->next, struct inode, i_sb_list);
>> + if (it->flags & INODE_ITER_UNUSED) {
>> + if (icount_read_once(inode)) {
>> + it->next = it->next->next;
>> + continue;
>> + }
>> +
>> + spin_lock(&inode->i_lock);
>> + if (icount_read(inode)) {
>> + spin_unlock(&inode->i_lock);
>> + it->next = it->next->next;
>> + continue;
>> + }
>> + } else {
>> + spin_lock(&inode->i_lock);
>> + }
>> +
>> + if ((it->flags & INODE_ITER_NORMAL) &&
>> + (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE))) {
>> + spin_unlock(&inode->i_lock);
>> + it->next = it->next->next;
>> + continue;
>> + }
>> +
>> + it->next = it->next->next;
>> + ret = it->func(inode, it->data);
>> + if (ret) {
>> + it->ret = ret;
>> + return false;
>> + }
>> +
>> + if (need_resched()) {
>> + spin_unlock(&sb->s_inode_list_lock);
>> + cond_resched();
>> + spin_lock(&sb->s_inode_list_lock);
>> + }
>> + }
>> +
>> + return it->next == &sb->s_inodes ? false : true;
>> +}
>> +
>> +int sb_for_each_inodes(struct super_block *sb, unsigned int flags,
>> + inode_iter_cb fn, void *data)
>> +{
>> + struct inode_iter it;
>> +
>> + inode_sb_iter_start(sb, &it, flags, fn, data);
>> + while (inode_sb_iter_next(&it, sb))
>> + ;
>> + inode_sb_iter_end(&it, sb);
>> +
>> + return it.ret;
>> +}
>> +EXPORT_SYMBOL(sb_for_each_inodes);
>> +
>> static inline void inode_sb_list_del(struct inode *inode)
>> {
>> struct super_block *sb = inode->i_sb;
>> + struct inode_iter *it;
>>
>> if (!list_empty(&inode->i_sb_list)) {
>> spin_lock(&sb->s_inode_list_lock);
>> + list_for_each_entry(it, &sb->s_inodes_iters, iters_node) {
>> + if (it->next == &inode->i_sb_list)
>> + it->next = inode->i_sb_list.next;
>> + }
>> list_del_init(&inode->i_sb_list);
>> spin_unlock(&sb->s_inode_list_lock);
>> }
>> diff --git a/fs/super.c b/fs/super.c
>> index 05e443173038..3e069150c544 100644
>> --- a/fs/super.c
>> +++ b/fs/super.c
>> @@ -382,6 +382,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
>> spin_lock_init(&s->s_roots_lock);
>> mutex_init(&s->s_sync_lock);
>> INIT_LIST_HEAD(&s->s_inodes);
>> + INIT_LIST_HEAD(&s->s_inodes_iters);
>> spin_lock_init(&s->s_inode_list_lock);
>> INIT_LIST_HEAD(&s->s_inodes_wb);
>> spin_lock_init(&s->s_inode_wblist_lock);
>> diff --git a/include/linux/fs.h b/include/linux/fs.h
>> index 09c4db5e9ae0..f3176ab10e65 100644
>> --- a/include/linux/fs.h
>> +++ b/include/linux/fs.h
>> @@ -870,6 +870,21 @@ struct inode {
>> void *i_private; /* fs or device private pointer */
>> } __randomize_layout;
>>
>> +enum inode_iter_flags_enum {
>> + INODE_ITER_NORMAL = (1U << 1), /* Exclude inodes with (I_NEW | I_FREEING | I_WILL_FREE). */
>> + INODE_ITER_UNUSED = (1U << 2), /* Only return inodes with (i_count == 0). */
>> +};
>> +
>> +/*
>> + * start end
>> + * inode->i_lock locked unlocked
>> + * sb->s_inode_list_lock locked locked
>> + */
>> +typedef int (*inode_iter_cb) (struct inode *, void *);
>> +
>> +int sb_for_each_inodes(struct super_block *sb, unsigned int flags,
>> + inode_iter_cb fn, void *data);
>> +
>> /*
>> * i_state handling
>> *
>> diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
>> index ecd96aeb1cee..1f81cc219b8e 100644
>> --- a/include/linux/fs/super_types.h
>> +++ b/include/linux/fs/super_types.h
>> @@ -269,9 +269,10 @@ struct super_block {
>> */
>> int s_stack_depth;
>>
>> - /* s_inode_list_lock protects s_inodes */
>> + /* s_inode_list_lock protects s_inodes and s_inodes_iters */
>> spinlock_t s_inode_list_lock ____cacheline_aligned_in_smp;
>> struct list_head s_inodes; /* all inodes */
>> + struct list_head s_inodes_iters; /* all iterators */
>>
>> spinlock_t s_inode_wblist_lock;
>> struct list_head s_inodes_wb; /* writeback inodes */
>> --
>> 2.39.5
>>
Thanks,
--
Julian Sun <sunjunchao@bytedance.com>
next prev parent reply other threads:[~2026-09-11 3:35 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 9:01 [PATCH 0/7] fs: preserve superblock inode walk positions across lock drops Julian Sun
2026-09-09 9:01 ` [PATCH 1/7] fs: remove trailing whitespace from include/linux/fs.h Julian Sun
2026-09-10 16:52 ` Jan Kara
2026-09-09 9:01 ` [PATCH 2/7] fs: introduce sb_for_each_inodes() Julian Sun
2026-09-10 17:47 ` Jan Kara
2026-09-11 3:34 ` [External] " Julian Sun
2026-09-11 3:35 ` Julian Sun [this message]
2026-09-09 9:01 ` [PATCH 3/7] block: use sb_for_each_inodes() in sync_bdevs() Julian Sun
2026-09-09 9:01 ` [PATCH 4/7] fs: use sb_for_each_inodes() API Julian Sun
2026-09-09 9:01 ` [PATCH 5/7] gfs2: use sb_for_each_inodes() for cooperative eviction Julian Sun
2026-09-09 9:01 ` [PATCH 6/7] quota: use sb_for_each_inodes() in add_dquot_ref() Julian Sun
2026-09-09 9:01 ` [PATCH 7/7] landlock: use sb_for_each_inodes() when detaching a superblock Julian Sun
2026-09-09 12:49 ` [PATCH 0/7] fs: preserve superblock inode walk positions across lock drops Jan Kara
2026-09-09 13:08 ` [External] " Julian Sun
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=1891f4ac-139e-4e9c-80ff-802ec2bb2844@bytedance.com \
--to=sunjunchao@bytedance.com \
--cc=agruenba@redhat.com \
--cc=aleksa@amutable.com \
--cc=djwong@kernel.org \
--cc=ebiggers@kernel.org \
--cc=gfs2@lists.linux.dev \
--cc=gnoack@google.com \
--cc=jack@suse.cz \
--cc=jmorris@namei.org \
--cc=legion@kernel.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=mic@digikod.net \
--cc=paul@paul-moore.com \
--cc=sandeen@redhat.com \
--cc=serge@hallyn.com \
/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