[PATCH 5/7] vfs: add inode iteration superblock method

Dave Chinner
2024-10-02T01:40:23Z

From: Dave Chinner <dchinner@redhat.com>

For filesytsems that provide their own inode cache that can be
traversed, add a sueprblock method that can be used instead of
iterating the sb->s_inodes list. This allows these filesystems to
avoid having to populate the sb->s_inodes list and hence avoid the
scalability limitations that this list imposes.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
fs/super.c | 54 +++++++++++++++++++++++++++++++---------------
include/linux/fs.h | 4 ++++
2 files changed, 41 insertions(+), 17 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index 20a9446d943a..971ad4e996e0 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -167,6 +167,31 @@ static void super_wake(struct super_block *sb, unsigned int flag)
wake_up_var(&sb->s_flags);
}

+bool super_iter_iget(struct inode *inode, int flags)
+{
+ bool ret = false;
+
+ spin_lock(&inode->i_lock);
+ if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE))
+ goto out_unlock;
+
+ /*
+ * Skip over zero refcount inode if the caller only wants
+ * referenced inodes to be iterated.
+ */
+ if ((flags & INO_ITER_REFERENCED) &&
+ !atomic_read(&inode->i_count))
+ goto out_unlock;
+
+ __iget(inode);
+ ret = true;
+out_unlock:
+ spin_unlock(&inode->i_lock);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(super_iter_iget);
+
/**
* super_iter_inodes - iterate all the cached inodes on a superblock
* @sb: superblock to iterate
@@ -184,26 +209,15 @@ int super_iter_inodes(struct super_block *sb, ino_iter_fn iter_fn,
struct inode *inode, *old_inode = NULL;
int ret = 0;

+ if (sb->s_op->iter_vfs_inodes) {
+ return sb->s_op->iter_vfs_inodes(sb, iter_fn,
+ private_data, flags);
+ }
+
spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
- spin_lock(&inode->i_lock);
- if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
- spin_unlock(&inode->i_lock);
+ if (!super_iter_iget(inode, flags))
continue;
- }
-
- /*
- * Skip over zero refcount inode if the caller only wants
- * referenced inodes to be iterated.
- */
- if ((flags & INO_ITER_REFERENCED) &&
- !atomic_read(&inode->i_count)) {
- spin_unlock(&inode->i_lock);
- continue;
- }
-
- __iget(inode);
- spin_unlock(&inode->i_lock);
spin_unlock(&sb->s_inode_list_lock);
iput(old_inode);

@@ -261,6 +275,12 @@ void super_iter_inodes_unsafe(struct super_block *sb, ino_iter_fn iter_fn,
struct inode *inode;
int ret;

+ if (sb->s_op->iter_vfs_inodes) {
+ sb->s_op->iter_vfs_inodes(sb, iter_fn,
+ private_data, INO_ITER_UNSAFE);
+ return;
+ }
+
rcu_read_lock();
spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0a6a462c45ab..8e82e3dc0618 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2224,6 +2224,7 @@ enum freeze_holder {
typedef int (*ino_iter_fn)(struct inode *inode, void *priv);
int super_iter_inodes(struct super_block *sb, ino_iter_fn iter_fn,
void *private_data, int flags);
+bool super_iter_iget(struct inode *inode, int flags);

struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
@@ -2258,6 +2259,9 @@ struct super_operations {
long (*free_cached_objects)(struct super_block *,
struct shrink_control *);
void (*shutdown)(struct super_block *sb);
+
+ int (*iter_vfs_inodes)(struct super_block *sb, ino_iter_fn iter_fn,
+ void *private_data, int flags);
};

/*
--
2.45.2