From: Huang Shijie <huangsj@hygon.cn>
To: <axboe@kernel.dk>, <viro@zeniv.linux.org.uk>,
<brauner@kernel.org>, <jack@suse.cz>
Cc: <zhongyuan@hygon.cn>, <fangbaoshun@hygon.cn>,
<yingzhiwei@hygon.cn>, <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>,
<david@kernel.org>, <sandeen@redhat.com>, <ebiggers@kernel.org>,
<linux-block@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<linux-fsdevel@vger.kernel.org>, <gfs2@lists.linux.dev>,
<linux-security-module@vger.kernel.org>,
<sunjunchao@bytedance.com>, <shijie8@gmail.com>,
Huang Shijie <huangsj@hygon.cn>
Subject: [PATCH v2 1/3] vfs: add inode iteration methods
Date: Fri, 13 Nov 2026 19:57:00 +0800 [thread overview]
Message-ID: <20261113115702.586594-2-huangsj@hygon.cn> (raw)
In-Reply-To: <20261113115702.586594-1-huangsj@hygon.cn>
Add two inode iteration methods:
super_iter_inodes_unsafe()/super_iter_inodes()
We use them to replace the explicit sb->s_inodes iteration,
with the caller supplying an inode_iter_context which has the
metadata needed in the interation.
The two iteration methods:
-- super_iter_inodes() runs @ctx->iter_fn() with a referenced
inode and with sb->s_inode_list_lock dropped, so the callback
is free to block and to do whatever it wants with the inode
except drop the reference the iterator holds. The iterator
releases that one reference per inode itself, so the callback
cannot leave a dangling inode behind. It is exported because
filesystems built as modules need it.
-- super_iter_inodes_unsafe() keeps sb->s_inode_list_lock and
rcu_read_lock() held for the whole walk and takes no inode
references at all. Its callback therefore only has an
existence guarantee: it must not block and must do its own
inode state checks and its own reference handling. It is
internal to the VFS, being declared in fs/internal.h.
Both take a struct inode_iter_context carrying the per-caller
state:
iter_fn the callback. Returns INO_ITER_RET_NEXT to go on
with the next inode, or INO_ITER_RET_ABORT to stop.
eligible optional filter, called only by the safe iterator,
from inside the inode's i_lock and before __iget(),
so that an inode can be rejected without paying for
a reference.
prev resume point. A caller of the unsafe iterator which
has to drop the list lock in the middle of a walk
stores the inode to continue from here, and the next
call starts after that inode instead of at the head.
flags INO_ITER_F_REFERENCED makes the safe iterator skip
inodes which have no reference, which is what
hook_sb_delete() filters for today.
priv caller private data.
This patch only adds the methods; converting the callers over is
left to the patches which follow.
Signed-off-by: Huang Shijie <huangsj@hygon.cn>
---
fs/internal.h | 34 ++++++++++++++++++
fs/super.c | 65 ++++++++++++++++++++++++++++++++++
include/linux/fs/super_types.h | 25 +++++++++++++
3 files changed, 124 insertions(+)
diff --git a/fs/internal.h b/fs/internal.h
index 967c8556c17f..f9b39d05bbee 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -139,6 +139,40 @@ struct super_block *user_get_super(dev_t, bool excl);
void put_super(struct super_block *sb);
extern bool mount_capable(struct fs_context *);
+/*
+ * Unsafe iteration for internal VFS use only:
+ * @ctx->iter_fn runs in atomic context with only
+ * an inode existence guarantee, so it must not block
+ * and must perform its own inode state checks.
+ *
+ * Return INO_ITER_RET_ABORT if the iteration stops early.
+ * Return INO_ITER_RET_DONE if the iteration runs to the end.
+ */
+static inline int super_iter_inodes_unsafe(struct super_block *sb,
+ struct inode_iter_context *ctx)
+{
+ struct inode *inode;
+ int ret = INO_ITER_RET_NEXT;
+
+ rcu_read_lock();
+ spin_lock(&sb->s_inode_list_lock);
+ inode = list_prepare_entry(ctx->prev, &sb->s_inodes, i_sb_list);
+
+ list_for_each_entry_continue(inode, &sb->s_inodes, i_sb_list) {
+ ret = ctx->iter_fn(inode, ctx);
+ if (ret == INO_ITER_RET_ABORT)
+ break;
+ }
+ spin_unlock(&sb->s_inode_list_lock);
+ rcu_read_unlock();
+
+ /* Mark the end here */
+ if (ret == INO_ITER_RET_NEXT)
+ ret = INO_ITER_RET_DONE;
+
+ return ret;
+}
+
/*
* Prepare superblock for changing its read-only state (i.e., either remount
* read-write superblock read-only or vice versa). After this function returns
diff --git a/fs/super.c b/fs/super.c
index c5a0658f7aaf..32137422df35 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -171,6 +171,71 @@ static void super_wake(struct super_block *sb, unsigned int flag)
wake_up_var(&sb->s_flags);
}
+static bool super_iter_iget(struct inode *inode, struct inode_iter_context *ctx)
+{
+ bool ret = false;
+
+ if ((ctx->flags & INO_ITER_F_REFERENCED) && !icount_read_once(inode))
+ return false;
+
+ spin_lock(&inode->i_lock);
+ if (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE))
+ goto out_unlock;
+
+ if (ctx->eligible && !ctx->eligible(inode, ctx))
+ goto out_unlock;
+
+ __iget(inode);
+ ret = true;
+out_unlock:
+ spin_unlock(&inode->i_lock);
+ return ret;
+}
+
+/**
+ * super_iter_inodes - iterate all the cached inodes on a superblock
+ * @sb: superblock to iterate
+ * @ctx: the context which has the metadata
+ *
+ * This function iterates all cached inodes on a superblock that are not in
+ * the process of being initialised or torn down. It will run @ctx->iter_fn()
+ * with a valid, referenced inode, so it is safe for the caller to do
+ * anything it wants with the inode except drop the reference the iterator
+ * holds.
+ *
+ * Return INO_ITER_RET_DONE if the iteration runs to the end.
+ * Return other value if the iteration breaks.
+ */
+int super_iter_inodes(struct super_block *sb, struct inode_iter_context *ctx)
+{
+ struct inode *inode, *old_inode = NULL;
+ int ret;
+
+ spin_lock(&sb->s_inode_list_lock);
+ list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
+ if (!super_iter_iget(inode, ctx))
+ continue;
+
+ spin_unlock(&sb->s_inode_list_lock);
+ iput(old_inode);
+
+ ret = ctx->iter_fn(inode, ctx);
+
+ old_inode = inode;
+ if (ret != INO_ITER_RET_NEXT)
+ goto out_loop;
+
+ cond_resched();
+ spin_lock(&sb->s_inode_list_lock);
+ }
+ spin_unlock(&sb->s_inode_list_lock);
+ ret = INO_ITER_RET_DONE;
+out_loop:
+ iput(old_inode);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(super_iter_inodes);
+
/*
* One thing we have to be careful of with a per-sb shrinker is that we don't
* drop the last active reference to the superblock from within the shrinker.
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index 72f4288e16c5..da15c360def3 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -80,6 +80,31 @@ enum freeze_holder {
FREEZE_EXCL = (1U << 3),
};
+/* Inode iteration callback return values */
+#define INO_ITER_RET_DONE 0
+#define INO_ITER_RET_ABORT 1
+#define INO_ITER_RET_NEXT 2
+
+/* Inode iteration control flags */
+/* Only iterate inodes that still have a reference. */
+#define INO_ITER_F_REFERENCED (1U << 0)
+
+struct inode_iter_context {
+ /* the hook for the iteration */
+ int (*iter_fn)(struct inode *inode, struct inode_iter_context *ctx);
+
+ /* the hook for the eligibility check, called before __iget() */
+ bool (*eligible)(struct inode *inode, struct inode_iter_context *ctx);
+
+ /* Save the temporary inode in a loop break */
+ struct inode *prev;
+
+ int flags;
+ void *priv;
+};
+
+int super_iter_inodes(struct super_block *sb, struct inode_iter_context *ctx);
+
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *inode);
--
2.53.0
next prev parent reply other threads:[~2026-11-13 11:57 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 11:56 [PATCH v2 0/3] vfs: add inode iteration methods Huang Shijie
2026-11-13 11:57 ` Huang Shijie [this message]
2026-11-13 12:23 ` [PATCH v2 1/3] " Christoph Hellwig
2026-11-13 11:57 ` [PATCH v2 2/3] vfs: convert inode iterators to super_iter_inodes_unsafe() Huang Shijie
2026-11-13 11:57 ` [PATCH v2 3/3] vfs: convert sb->s_inodes iteration to super_iter_inodes() Huang Shijie
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=20261113115702.586594-2-huangsj@hygon.cn \
--to=huangsj@hygon.cn \
--cc=agruenba@redhat.com \
--cc=aleksa@amutable.com \
--cc=axboe@kernel.dk \
--cc=brauner@kernel.org \
--cc=david@kernel.org \
--cc=ebiggers@kernel.org \
--cc=fangbaoshun@hygon.cn \
--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-kernel@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 \
--cc=shijie8@gmail.com \
--cc=sunjunchao@bytedance.com \
--cc=viro@zeniv.linux.org.uk \
--cc=yingzhiwei@hygon.cn \
--cc=zhongyuan@hygon.cn \
/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