All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] fs/proc: split the inode list for procfs
@ 2026-09-14  6:14 Huang Shijie
  2026-09-14  6:14 ` [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb() Huang Shijie
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Huang Shijie @ 2026-09-14  6:14 UTC (permalink / raw)
  To: viro, brauner
  Cc: zhongyuan, fangbaoshun, yingzhiwei, jack, dev.jain, ljs,
	adobriyan, akpm, hpa, ryan.roberts, mjguzik, joel.granados, jannh,
	oleg, aleksa, legion, kees, joannelkoong, tj, libaokun, ebiggers,
	sandeen, linux-fsdevel, linux-kernel, Huang Shijie

The global s_inode_list_lock is heavily contended in procfs
on a 384-CPU, 12-NUMA-node Hygon machine running Hadoop TestDFSIO:
  #hadoop jar xxxx.jar TestDFSIO -read -nrFiles 1000 -size 100MB

The hadoop will create lot of threads during the test.
The perf shows it consuming ~90% of the lock hotspot in procfs.
The lock is hit from both directions:
   -- inode creation (~49%) :
           getdents64 ->
              proc_readfd_common ->
	         new_inode ->
		   inode_sb_list_add()

   -- inode eviction (~41%):
           process exit ->
	      release_task ->
	         proc_invalidate_siblings_dcache ->
		     evict ->
		        inode_sb_list_del()

This patch set tries to resolve this issue by:
    patch 1: "fs/drop_caches: do not scan procfs in drop_pagecache_sb()"
       This patch skips the scan for procfs in drop caches.

    patch 2: Add a new helper to detect the empty inode list    

    patch 3: split the inode list.


After this patch, the above TestDFSIO can get over 50% better performance
in exec time. And the procfs lock hotspot becomes nearly 1%.

Huang Shijie (3):
  fs/drop_caches: do not scan procfs in drop_pagecache_sb()
  fs/super: introduce a helper sb_inodes_empty()
  fs/proc: split the inode list

 fs/drop_caches.c               |  4 ++
 fs/inode.c                     | 80 ++++++++++++++++++++++------------
 fs/proc/inode.c                | 53 ++++++++++++++++++++++
 fs/proc/internal.h             |  2 +
 fs/proc/root.c                 |  6 +++
 fs/super.c                     | 45 +++++++++++++++----
 include/linux/fs/super_types.h | 22 +++++++++-
 include/linux/proc_fs.h        |  1 +
 8 files changed, 176 insertions(+), 37 deletions(-)

-- 
2.53.0



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

* [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb()
  2026-09-14  6:14 [RFC PATCH 0/3] fs/proc: split the inode list for procfs Huang Shijie
@ 2026-09-14  6:14 ` Huang Shijie
  2026-09-14  9:19   ` Alexey Dobriyan
  2026-09-14  6:14 ` [RFC PATCH 2/3] fs/super: introduce a helper sb_inodes_empty() Huang Shijie
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Huang Shijie @ 2026-09-14  6:14 UTC (permalink / raw)
  To: viro, brauner
  Cc: zhongyuan, fangbaoshun, yingzhiwei, jack, dev.jain, ljs,
	adobriyan, akpm, hpa, ryan.roberts, mjguzik, joel.granados, jannh,
	oleg, aleksa, legion, kees, joannelkoong, tj, libaokun, ebiggers,
	sandeen, linux-fsdevel, linux-kernel, Huang Shijie

There is no page cache for the procfs, so do not scan the
inodes in procfs.

Signed-off-by: Huang Shijie <huangsj@hygon.cn>
---
 fs/drop_caches.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fs/drop_caches.c b/fs/drop_caches.c
index 49f56a598ecb..1959090089af 100644
--- a/fs/drop_caches.c
+++ b/fs/drop_caches.c
@@ -5,6 +5,7 @@
 
 #include <linux/pagemap.h>
 #include <linux/kernel.h>
+#include <linux/magic.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/writeback.h>
@@ -20,6 +21,9 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
 {
 	struct inode *inode, *toput_inode = NULL;
 
+	if (sb->s_magic == PROC_SUPER_MAGIC)
+		return;
+
 	spin_lock(&sb->s_inode_list_lock);
 	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
 		spin_lock(&inode->i_lock);
-- 
2.53.0



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

* [RFC PATCH 2/3] fs/super: introduce a helper sb_inodes_empty()
  2026-09-14  6:14 [RFC PATCH 0/3] fs/proc: split the inode list for procfs Huang Shijie
  2026-09-14  6:14 ` [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb() Huang Shijie
@ 2026-09-14  6:14 ` Huang Shijie
  2026-09-14  6:14 ` [RFC PATCH 3/3] fs/proc: split the inode list Huang Shijie
  2026-09-14  8:50 ` [syzbot ci] Re: fs/proc: split the inode list for procfs syzbot ci
  3 siblings, 0 replies; 8+ messages in thread
From: Huang Shijie @ 2026-09-14  6:14 UTC (permalink / raw)
  To: viro, brauner
  Cc: zhongyuan, fangbaoshun, yingzhiwei, jack, dev.jain, ljs,
	adobriyan, akpm, hpa, ryan.roberts, mjguzik, joel.granados, jannh,
	oleg, aleksa, legion, kees, joannelkoong, tj, libaokun, ebiggers,
	sandeen, linux-fsdevel, linux-kernel, Huang Shijie

Introduce a helper sb_inodes_empty() which is used to
detect if the inodes list is empty.

Signed-off-by: Huang Shijie <huangsj@hygon.cn>
---
 fs/super.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/fs/super.c b/fs/super.c
index 05e443173038..4b1314545522 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -724,6 +724,11 @@ void retire_super(struct super_block *sb)
 }
 EXPORT_SYMBOL(retire_super);
 
+static bool sb_inodes_empty(struct super_block *sb)
+{
+	return list_empty(&sb->s_inodes);
+}
+
 /**
  *	generic_shutdown_super	-	common helper for ->kill_sb()
  *	@sb: superblock to kill
@@ -774,7 +779,7 @@ void generic_shutdown_super(struct super_block *sb)
 		 */
 		fscrypt_destroy_keyring(sb);
 
-		if (CHECK_DATA_CORRUPTION(!list_empty(&sb->s_inodes), NULL,
+		if (CHECK_DATA_CORRUPTION(!sb_inodes_empty(sb), NULL,
 				"VFS: Busy inodes after unmount of %s (%s)",
 				sb->s_id, sb->s_type->name)) {
 			/*
-- 
2.53.0



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

* [RFC PATCH 3/3] fs/proc: split the inode list
  2026-09-14  6:14 [RFC PATCH 0/3] fs/proc: split the inode list for procfs Huang Shijie
  2026-09-14  6:14 ` [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb() Huang Shijie
  2026-09-14  6:14 ` [RFC PATCH 2/3] fs/super: introduce a helper sb_inodes_empty() Huang Shijie
@ 2026-09-14  6:14 ` Huang Shijie
  2026-09-14  8:50 ` [syzbot ci] Re: fs/proc: split the inode list for procfs syzbot ci
  3 siblings, 0 replies; 8+ messages in thread
From: Huang Shijie @ 2026-09-14  6:14 UTC (permalink / raw)
  To: viro, brauner
  Cc: zhongyuan, fangbaoshun, yingzhiwei, jack, dev.jain, ljs,
	adobriyan, akpm, hpa, ryan.roberts, mjguzik, joel.granados, jannh,
	oleg, aleksa, legion, kees, joannelkoong, tj, libaokun, ebiggers,
	sandeen, linux-fsdevel, linux-kernel, Huang Shijie

The global s_inode_list_lock is heavily contended in procfs
on a 384-CPU, 12-NUMA-node Hygon machine running Hadoop TestDFSIO:
  #hadoop jar xxxx.jar TestDFSIO -read -nrFiles 1000 -size 100MB

The hadoop will create lot of threads during the test.
The perf shows it consuming ~90% of the lock hotspot in procfs.
The lock is hit from both directions:
   -- inode creation (~49%) :
           getdents64 ->
              proc_readfd_common ->
	         new_inode ->
		   inode_sb_list_add()

   -- inode eviction (~41%):
           process exit ->
	      release_task ->
	         proc_invalidate_siblings_dcache ->
		     evict ->
		        inode_sb_list_del()

This patch spreads the inode list across per-shard locks for procfs:
   --- Add "shards" pointer shares a union with s_inodes.
       Guard all shard accesses with an explicit s_inode_list_sharded
       field in super_block.

   --- Add inode_list_add()/inode_list_del() callbacks to super_operations;
       procfs implements them to round-robin inodes
       onto nr_shards = DIV_ROUND_UP(num_possible_cpus(), 32)
       shards allocated at mount time, each protected by its own spinlock.

   --- evict_inodes(), generic_shutdown_super()
       iterate the shards when the super_block inode list is sharded.
       struct inode_shard is cacheline-aligned to avoid false
       sharing between shard locks on different NUMA nodes.

This reduces the s_inode_list_lock hotspot from ~90% to ~1% in above
TestDFSIO. And we can improve the hadoop exec performance over 50%.

Signed-off-by: Huang Shijie <huangsj@hygon.cn>
---
 fs/inode.c                     | 80 ++++++++++++++++++++++------------
 fs/proc/inode.c                | 53 ++++++++++++++++++++++
 fs/proc/internal.h             |  2 +
 fs/proc/root.c                 |  6 +++
 fs/super.c                     | 40 +++++++++++++----
 include/linux/fs/super_types.h | 22 +++++++++-
 include/linux/proc_fs.h        |  1 +
 7 files changed, 167 insertions(+), 37 deletions(-)

diff --git a/fs/inode.c b/fs/inode.c
index ba7da39be4a3..687c2e299258 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -635,6 +635,10 @@ void inode_sb_list_add(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 
+	if (sb->s_inode_list_sharded && sb->s_op->inode_list_add) {
+		sb->s_op->inode_list_add(sb, inode);
+		return;
+	}
 	spin_lock(&sb->s_inode_list_lock);
 	list_add(&inode->i_sb_list, &sb->s_inodes);
 	spin_unlock(&sb->s_inode_list_lock);
@@ -646,6 +650,10 @@ static inline void inode_sb_list_del(struct inode *inode)
 	struct super_block *sb = inode->i_sb;
 
 	if (!list_empty(&inode->i_sb_list)) {
+		if (sb->s_inode_list_sharded && sb->s_op->inode_list_del) {
+			sb->s_op->inode_list_del(sb, inode);
+			return;
+		}
 		spin_lock(&sb->s_inode_list_lock);
 		list_del_init(&inode->i_sb_list);
 		spin_unlock(&sb->s_inode_list_lock);
@@ -879,41 +887,57 @@ void evict_inodes(struct super_block *sb)
 {
 	struct inode *inode;
 	LIST_HEAD(dispose);
+	struct list_head *head;
+	spinlock_t *lock;
+	unsigned int nr, i;
+	const bool sharded = sb->s_inode_list_sharded;
 
-again:
-	spin_lock(&sb->s_inode_list_lock);
-	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
-		if (icount_read_once(inode))
-			continue;
+	nr = sharded ? sb->nr_shards : 1;
 
-		spin_lock(&inode->i_lock);
-		if (icount_read(inode)) {
-			spin_unlock(&inode->i_lock);
-			continue;
-		}
-		if (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE)) {
-			spin_unlock(&inode->i_lock);
-			continue;
+	for (i = 0; i < nr; i++) {
+		if (sharded) {
+			head = &sb->shards[i].list;
+			lock = &sb->shards[i].lock;
+		} else {
+			head = &sb->s_inodes;
+			lock = &sb->s_inode_list_lock;
 		}
 
-		inode_state_set(inode, I_FREEING);
-		inode_lru_list_del(inode);
-		spin_unlock(&inode->i_lock);
-		list_add(&inode->i_lru, &dispose);
+again:
+		spin_lock(lock);
+		list_for_each_entry(inode, head, i_sb_list) {
+			if (icount_read_once(inode))
+				continue;
 
-		/*
-		 * We can have a ton of inodes to evict at unmount time given
-		 * enough memory, check to see if we need to go to sleep for a
-		 * bit so we don't livelock.
-		 */
-		if (need_resched()) {
-			spin_unlock(&sb->s_inode_list_lock);
-			cond_resched();
-			dispose_list(&dispose);
-			goto again;
+			spin_lock(&inode->i_lock);
+			if (icount_read(inode)) {
+				spin_unlock(&inode->i_lock);
+				continue;
+			}
+			if (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE)) {
+				spin_unlock(&inode->i_lock);
+				continue;
+			}
+
+			inode_state_set(inode, I_FREEING);
+			inode_lru_list_del(inode);
+			spin_unlock(&inode->i_lock);
+			list_add(&inode->i_lru, &dispose);
+
+			/*
+			 * We can have a ton of inodes to evict at unmount time given
+			 * enough memory, check to see if we need to go to sleep for a
+			 * bit so we don't livelock.
+			 */
+			if (need_resched()) {
+				spin_unlock(lock);
+				cond_resched();
+				dispose_list(&dispose);
+				goto again;
+			}
 		}
+		spin_unlock(lock);
 	}
-	spin_unlock(&sb->s_inode_list_lock);
 
 	dispose_list(&dispose);
 }
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index b7634f975d98..d412018b73d9 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -65,6 +65,7 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
 	ei->sysctl_entry = NULL;
 	INIT_HLIST_NODE(&ei->sibling_inodes);
 	ei->ns_ops = NULL;
+	ei->shard_idx = 0;
 	return &ei->vfs_inode;
 }
 
@@ -80,6 +81,56 @@ static void proc_free_inode(struct inode *inode)
 	kmem_cache_free(proc_inode_cachep, PROC_I(inode));
 }
 
+static void proc_inode_list_add(struct super_block *sb, struct inode *inode)
+{
+	struct proc_fs_info *fs_info = proc_sb_info(sb);
+	struct proc_inode *ei = PROC_I(inode);
+	struct inode_shard *shard;
+	unsigned int idx, seq;
+
+	seq = atomic_fetch_inc(&fs_info->shard_seq);
+	idx = seq % sb->nr_shards;
+	ei->shard_idx = idx;
+	shard = &sb->shards[idx];
+
+	spin_lock(&shard->lock);
+	list_add(&inode->i_sb_list, &shard->list);
+	spin_unlock(&shard->lock);
+}
+
+static void proc_inode_list_del(struct super_block *sb, struct inode *inode)
+{
+	struct proc_inode *ei = PROC_I(inode);
+	struct inode_shard *shard = &sb->shards[ei->shard_idx];
+
+	spin_lock(&shard->lock);
+	list_del_init(&inode->i_sb_list);
+	spin_unlock(&shard->lock);
+}
+
+#define PROC_LIST_ALIGN	32
+int proc_init_inode_shards(struct super_block *sb)
+{
+	struct inode_shard *shards;
+	unsigned int nr_shards;
+	int i;
+
+	nr_shards = DIV_ROUND_UP(num_possible_cpus(), PROC_LIST_ALIGN);
+	shards = kcalloc(nr_shards, sizeof(*shards), GFP_KERNEL);
+	if (!shards)
+		return -ENOMEM;
+
+	for (i = 0; i < nr_shards; i++) {
+		INIT_LIST_HEAD(&shards[i].list);
+		spin_lock_init(&shards[i].lock);
+	}
+
+	sb->shards = shards;
+	sb->nr_shards = nr_shards;
+	sb->s_inode_list_sharded = true;
+	return 0;
+}
+
 static void init_once(void *foo)
 {
 	struct proc_inode *ei = (struct proc_inode *) foo;
@@ -191,6 +242,8 @@ const struct super_operations proc_sops = {
 	.evict_inode	= proc_evict_inode,
 	.statfs		= simple_statfs,
 	.show_options	= proc_show_options,
+	.inode_list_add	= proc_inode_list_add,
+	.inode_list_del	= proc_inode_list_del,
 };
 
 enum {BIAS = -1U<<31};
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 04bd6c9e65a7..edbe357263c2 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -127,6 +127,7 @@ struct proc_inode {
 	struct hlist_node sibling_inodes;
 	const struct proc_ns_operations *ns_ops;
 	struct inode vfs_inode;
+	unsigned int shard_idx;
 } __randomize_layout;
 
 /*
@@ -317,6 +318,7 @@ void proc_init_kmemcache(void);
 void proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock);
 void set_proc_pid_nlink(void);
 extern struct inode *proc_get_inode(struct super_block *, struct proc_dir_entry *);
+extern int proc_init_inode_shards(struct super_block *);
 extern void proc_entry_rundown(struct proc_dir_entry *);
 
 /*
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 99adddfeb4a4..4bd916b88cdf 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -275,6 +275,10 @@ static int proc_fill_super(struct super_block *s, struct fs_context *fc)
 	s->s_time_gran = 1;
 	s->s_fs_info = fs_info;
 
+	ret = proc_init_inode_shards(s);
+	if (ret)
+		return ret;
+
 	if (fs_info->pidonly == PROC_PIDONLY_ON)
 		s->s_iflags |= SB_I_RESTRICTED_VARIANT;
 
@@ -359,6 +363,8 @@ static void proc_kill_sb(struct super_block *sb)
 	struct proc_fs_info *fs_info = proc_sb_info(sb);
 
 	kill_anon_super(sb);
+	if (sb->s_inode_list_sharded)
+		kfree(sb->shards);
 	if (fs_info) {
 		put_pid_ns(fs_info->pid_ns);
 		put_cred(fs_info->mounter_cred);
diff --git a/fs/super.c b/fs/super.c
index 4b1314545522..38a452e78ca2 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -726,7 +726,16 @@ EXPORT_SYMBOL(retire_super);
 
 static bool sb_inodes_empty(struct super_block *sb)
 {
-	return list_empty(&sb->s_inodes);
+	unsigned int i;
+
+	if (!sb->s_inode_list_sharded)
+		return list_empty(&sb->s_inodes);
+
+	for (i = 0; i < sb->nr_shards; i++)
+		if (!list_empty(&sb->shards[i].list))
+			return false;
+
+	return true;
 }
 
 /**
@@ -788,14 +797,29 @@ void generic_shutdown_super(struct super_block *sb)
 			 * iput_final() or such crashes cleanly.
 			 */
 			struct inode *inode;
-
-			spin_lock(&sb->s_inode_list_lock);
-			list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
-				inode->i_op = VFS_PTR_POISON;
-				inode->i_sb = VFS_PTR_POISON;
-				inode->i_mapping = VFS_PTR_POISON;
+			struct list_head *head;
+			spinlock_t *lock;
+			unsigned int nr, i;
+			const bool sharded = sb->s_inode_list_sharded;
+
+			nr = sharded ? sb->nr_shards : 1;
+			for (i = 0; i < nr; i++) {
+				if (sharded) {
+					head = &sb->shards[i].list;
+					lock = &sb->shards[i].lock;
+				} else {
+					head = &sb->s_inodes;
+					lock = &sb->s_inode_list_lock;
+				}
+
+				spin_lock(lock);
+				list_for_each_entry(inode, head, i_sb_list) {
+					inode->i_op = VFS_PTR_POISON;
+					inode->i_sb = VFS_PTR_POISON;
+					inode->i_mapping = VFS_PTR_POISON;
+				}
+				spin_unlock(lock);
 			}
-			spin_unlock(&sb->s_inode_list_lock);
 		}
 	}
 	/*
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index ecd96aeb1cee..6d6aae2d1fbc 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -130,8 +130,20 @@ struct super_operations {
 
 	/* Report a filesystem error */
 	void (*report_error)(const struct fserror_event *event);
+
+	void (*inode_list_add)(struct super_block *sb, struct inode *inode);
+	void (*inode_list_del)(struct super_block *sb, struct inode *inode);
 };
 
+/*
+ * Sharded inode list used by procfs to spread s_inode_list_lock contention
+ * across per-shard locks.
+ */
+struct inode_shard {
+	struct list_head	list;
+	spinlock_t		lock;
+} ____cacheline_aligned_in_smp;
+
 struct super_block {
 	struct list_head			s_list;		/* Keep this first */
 	dev_t					s_dev;		/* search index; _not_ kdev_t */
@@ -269,9 +281,17 @@ struct super_block {
 	 */
 	int s_stack_depth;
 
+	bool					s_inode_list_sharded;
+
 	/* s_inode_list_lock protects s_inodes */
 	spinlock_t				s_inode_list_lock ____cacheline_aligned_in_smp;
-	struct list_head			s_inodes;	/* all inodes */
+	union {
+		struct list_head			s_inodes;	/* all inodes */
+		struct {
+			struct inode_shard		*shards;	/* used by procfs */
+			unsigned int			nr_shards;
+		};
+	};
 
 	spinlock_t				s_inode_wblist_lock;
 	struct list_head			s_inodes_wb;	/* writeback inodes */
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 47d7deaeed8f..37eed34f3a93 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -70,6 +70,7 @@ struct proc_fs_info {
 	const struct cred *mounter_cred;
 	enum proc_hidepid hide_pid;
 	enum proc_pidonly pidonly;
+	atomic_t shard_seq;
 	struct rcu_head rcu;
 };
 
-- 
2.53.0



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

* [syzbot ci] Re: fs/proc: split the inode list for procfs
  2026-09-14  6:14 [RFC PATCH 0/3] fs/proc: split the inode list for procfs Huang Shijie
                   ` (2 preceding siblings ...)
  2026-09-14  6:14 ` [RFC PATCH 3/3] fs/proc: split the inode list Huang Shijie
@ 2026-09-14  8:50 ` syzbot ci
  3 siblings, 0 replies; 8+ messages in thread
From: syzbot ci @ 2026-09-14  8:50 UTC (permalink / raw)
  To: adobriyan, akpm, aleksa, brauner, dev.jain, ebiggers, fangbaoshun,
	hpa, huangsj, jack, jannh, joannelkoong, joel.granados, kees,
	legion, libaokun, linux-fsdevel, linux-kernel, ljs, mjguzik, oleg,
	ryan.roberts, sandeen, tj, viro, yingzhiwei, zhongyuan
  Cc: syzbot, syzkaller-bugs

syzbot ci has tested the following series

[v1] fs/proc: split the inode list for procfs
https://lore.kernel.org/all/20260914061449.4024632-1-huangsj@hygon.cn
* [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb()
* [RFC PATCH 2/3] fs/super: introduce a helper sb_inodes_empty()
* [RFC PATCH 3/3] fs/proc: split the inode list

and found the following issue:
KASAN: slab-out-of-bounds Read in hook_sb_delete

Full report is available here:
https://ci.syzbot.org/series/736afa16-0dc2-40e7-9a6c-74db419044d7

***

KASAN: slab-out-of-bounds Read in hook_sb_delete

tree:      vfs
URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/vfs/vfs.git
base:      15aa70de9f706fb0e3852f636b8a0a9d5bc048ab
arch:      amd64
compiler:  Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8
config:    https://ci.syzbot.org/builds/cd4bf6c8-aad6-4b38-b003-31dcd0a8a4bf/config

ima: No TPM chip found, activating TPM-bypass!
Loading compiled-in module X.509 certificates
Loaded X.509 cert 'Build time autogenerated kernel key: affe42cfbb2369724f6c4c4a3fe3f0b2fe4692dc'
ima: Allocated hash algorithm: sha256
ima: No architecture policies found
evm: Initialising EVM extended attributes:
evm: security.selinux (disabled)
evm: security.SMACK64 (disabled)
evm: security.SMACK64EXEC (disabled)
evm: security.SMACK64TRANSMUTE (disabled)
evm: security.SMACK64MMAP (disabled)
evm: security.apparmor
evm: security.ima
evm: security.capability
evm: HMAC attrs: 0x1
PM:   Magic number: 10:739:371
usb usb44-port5: hash matches
udc dummy_udc.27: hash matches
dummy_udc dummy_udc.27: hash matches
usb usb12-port1: hash matches
netconsole: network logging started
gtp: GTP module loaded (pdp ctx size 128 bytes)
rdma_rxe: loaded
cfg80211: Loading compiled-in X.509 certificates for regulatory database
Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
Loaded X.509 cert 'wens: 61c038651aabdcf94bd0ac7ff06c7248db18c600'
clk: Disabling unused clocks
ALSA device list:
  #0: Dummy 1
  #1: Loopback 1
  #2: Virtual MIDI Card 1
md: Waiting for all devices to be available before autodetect
md: If you don't use raid, use raid=noautodetect
md: Autodetecting RAID arrays.
md: autorun ...
md: ... autorun DONE.
EXT4-fs (sda1): mounted filesystem b4773fba-1738-4da0-8a90-0fe043d0a496 ro with ordered data mode. Quota mode: none.
VFS: Mounted root (ext4 filesystem) readonly on device 8:1.
devtmpfs: mounted
VFS: Pivoted into new rootfs
Freeing unused kernel image (initmem) memory: 27172K
Write protecting the kernel read-only data: 223232k
Freeing unused kernel image (text/rodata gap) memory: 1552K
Freeing unused kernel image (rodata/data gap) memory: 804K
x86/mm: Checked W+X mappings: passed, no W+X pages found.
x86/mm: Checking user space page tables
x86/mm: Checked W+X mappings: passed, no W+X pages found.
Failed to set sysctl parameter 'max_rcu_stall_to_panic=1': parameter not found
Run /sbin/init as init process
==================================================================
BUG: KASAN: slab-out-of-bounds in _raw_spin_lock+0x2e/0x40
Read of size 1 at addr ffff88810eea9ce0 by task init/1

CPU: 0 UID: 0 PID: 1 Comm: init Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
Call Trace:
 <TASK>
 dump_stack_lvl+0xe8/0x150
 print_address_description+0x55/0x1e0
 print_report+0x58/0x70
 kasan_report+0x117/0x150
 __kasan_check_byte+0x2a/0x40
 lock_acquire+0x7d/0x350
 _raw_spin_lock+0x2e/0x40
 hook_sb_delete+0x15d/0xd10
 security_sb_delete+0x76/0x110
 generic_shutdown_super+0xb2/0x3d0
 kill_anon_super+0x3b/0x70
 proc_kill_sb+0x47/0x180
 deactivate_locked_super+0xbc/0x110
 cleanup_mnt+0x3d3/0x460
 mntput_no_expire_slowpath+0xa58/0xbf0
 __fput+0x6a4/0xa50
 task_work_run+0x1d9/0x270
 exit_to_user_mode_loop+0x204/0x770
 ret_from_fork+0x593/0xb70
 ret_from_fork_asm+0x1a/0x30
 </TASK>

Allocated by task 1:
 kasan_save_track+0x3e/0x80
 __kasan_kmalloc+0x93/0xb0
 __kmalloc_flags_noprof+0x532/0x7a0
 __alloc_empty_sheaf+0x28/0x40
 __pcs_replace_empty_main+0x43b/0x6c0
 kmem_cache_alloc_noprof+0x399/0x600
 bio_alloc_bioset+0x271/0xc60
 ext4_mpage_readpages+0x12a7/0x1c40
 read_pages+0x193/0x5a0
 page_cache_ra_unbounded+0x754/0x9d0
 page_cache_ra_order+0xb47/0xee0
 filemap_get_pages+0x86d/0x1ec0
 filemap_read+0x426/0x11e0
 ext4_file_read_iter+0x553/0xad0
 __kernel_read+0x4ca/0x960
 integrity_kernel_read+0x89/0xd0
 ima_calc_file_hash+0x451/0x890
 ima_collect_measurement+0x51b/0xa00
 process_measurement+0x1272/0x1c10
 ima_file_mmap+0x1b0/0x200
 security_mmap_file+0x773/0xa20
 vm_mmap_pgoff+0x134/0x4e0
 elf_load+0x237/0x6a0
 load_elf_interp+0x4da/0xb50
 load_elf_binary+0x1bda/0x28d0
 bprm_execve+0x930/0x1590
 kernel_execve+0x8c3/0x9c0
 try_to_run_init_process+0x13/0x60
 kernel_init+0xb2/0x1d0
 ret_from_fork+0x514/0xb70
 ret_from_fork_asm+0x1a/0x30

The buggy address belongs to the object at ffff88810eea9c00
 which belongs to the cache kmalloc-128 of size 128
The buggy address is located 96 bytes to the right of
 allocated 128-byte region [ffff88810eea9c00, ffff88810eea9c80)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x10eea9
flags: 0x17ff00000000000(node=0|zone=2|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 017ff00000000000 ffff888100041a00 dead000000000122 0000000000000000
raw: 0000000000000000 0000000000100010 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0xd2c40(GFP_NOFS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 1, tgid 1 (swapper/0), ts 18944299736
 post_alloc_hook+0x1f9/0x250
 get_page_from_freelist+0x2209/0x2280
 __alloc_frozen_pages_noprof+0x217/0x5a0
 allocate_slab+0x7d/0x620
 refill_objects+0x2d5/0x350
 __pcs_replace_empty_main+0x2c8/0x6c0
 __kmalloc_noprof+0x47b/0x720
 tomoyo_commit_ok+0x29/0x1d0
 tomoyo_update_domain+0x4ea/0x7e0
 tomoyo_update_mount_acl+0x11c/0x2d0
 tomoyo_write_file+0xae4/0xc50
 tomoyo_supervisor+0xff8/0x1560
 tomoyo_mount_permission+0x670/0x9e0
 security_sb_mount+0xe4/0x320
 path_mount+0xbc/0x1050
 init_mount+0xc3/0x110
page_owner free stack trace missing

Memory state around the buggy address:
 ffff88810eea9b80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
 ffff88810eea9c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88810eea9c80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
                                                       ^
 ffff88810eea9d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ffff88810eea9d80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
  Tested-by: syzbot@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.

To test a fix for this bug, please reply with `#syz test`
(on a separate line) and attach the patch to the email.

Notes:
- The patch will be applied on top of the tested series (as an
  incremental fix).
- To test a new version of the whole series, please send it directly
  to syzbot@lists.linux.dev.
- Arguments like custom git repos and branches are not supported.

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

* Re: [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb()
  2026-09-14  6:14 ` [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb() Huang Shijie
@ 2026-09-14  9:19   ` Alexey Dobriyan
  2026-09-14 11:59     ` Huang Shijie
  0 siblings, 1 reply; 8+ messages in thread
From: Alexey Dobriyan @ 2026-09-14  9:19 UTC (permalink / raw)
  To: Huang Shijie
  Cc: viro, brauner, zhongyuan, fangbaoshun, yingzhiwei, jack, dev.jain,
	ljs, akpm, hpa, ryan.roberts, mjguzik, joel.granados, jannh, oleg,
	aleksa, legion, kees, joannelkoong, tj, libaokun, ebiggers,
	sandeen, linux-fsdevel, linux-kernel

On Mon, Sep 14, 2026 at 02:14:47PM +0800, Huang Shijie wrote:
> There is no page cache for the procfs, so do not scan the
> inodes in procfs.

> @@ -20,6 +21,9 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
>  {
>  	struct inode *inode, *toput_inode = NULL;
>  
> +	if (sb->s_magic == PROC_SUPER_MAGIC)
> +		return;

This should be some flag somewhere in superblock.
Other fake filesystems don't support pagecache too.

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

* Re: [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb()
  2026-09-14  9:19   ` Alexey Dobriyan
@ 2026-09-14 11:59     ` Huang Shijie
  2026-09-14 12:10       ` Huang Shijie
  0 siblings, 1 reply; 8+ messages in thread
From: Huang Shijie @ 2026-09-14 11:59 UTC (permalink / raw)
  To: Alexey Dobriyan
  Cc: viro, brauner, zhongyuan, fangbaoshun, yingzhiwei, jack, dev.jain,
	ljs, akpm, hpa, ryan.roberts, mjguzik, joel.granados, jannh, oleg,
	aleksa, legion, kees, joannelkoong, tj, libaokun, ebiggers,
	sandeen, linux-fsdevel, linux-kernel

On Mon, Sep 14, 2026 at 12:19:03PM +0300, Alexey Dobriyan wrote:
> On Mon, Sep 14, 2026 at 02:14:47PM +0800, Huang Shijie wrote:
> > There is no page cache for the procfs, so do not scan the
> > inodes in procfs.
> 
> > @@ -20,6 +21,9 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
> >  {
> >  	struct inode *inode, *toput_inode = NULL;
> >  
> > +	if (sb->s_magic == PROC_SUPER_MAGIC)
> > +		return;
> 
> This should be some flag somewhere in superblock.
> Other fake filesystems don't support pagecache too.
yes.

It seems it is better to check like this:
        if (sb->s_bdi == &noop_backing_dev_inf)
		return;



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

* Re: [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb()
  2026-09-14 11:59     ` Huang Shijie
@ 2026-09-14 12:10       ` Huang Shijie
  0 siblings, 0 replies; 8+ messages in thread
From: Huang Shijie @ 2026-09-14 12:10 UTC (permalink / raw)
  To: Alexey Dobriyan
  Cc: viro, brauner, zhongyuan, fangbaoshun, yingzhiwei, jack, dev.jain,
	ljs, akpm, hpa, ryan.roberts, mjguzik, joel.granados, jannh, oleg,
	aleksa, legion, kees, joannelkoong, tj, libaokun, ebiggers,
	sandeen, linux-fsdevel, linux-kernel

On Mon, Sep 14, 2026 at 07:59:09PM +0800, Huang Shijie wrote:
> On Mon, Sep 14, 2026 at 12:19:03PM +0300, Alexey Dobriyan wrote:
> > On Mon, Sep 14, 2026 at 02:14:47PM +0800, Huang Shijie wrote:
> > > There is no page cache for the procfs, so do not scan the
> > > inodes in procfs.
> > 
> > > @@ -20,6 +21,9 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
> > >  {
> > >  	struct inode *inode, *toput_inode = NULL;
> > >  
> > > +	if (sb->s_magic == PROC_SUPER_MAGIC)
> > > +		return;
> > 
> > This should be some flag somewhere in superblock.
> > Other fake filesystems don't support pagecache too.
> yes.
> 
> It seems it is better to check like this:
>         if (sb->s_bdi == &noop_backing_dev_inf)
> 		return;
> 
I find check noop_backing_dev_info is not right, the tmpfs may also use it.

It seems there is no flag in superblock for us.
If we have no choice, I can add a new flag for it.



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

end of thread, other threads:[~2026-09-14 12:10 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-14  6:14 [RFC PATCH 0/3] fs/proc: split the inode list for procfs Huang Shijie
2026-09-14  6:14 ` [RFC PATCH 1/3] fs/drop_caches: do not scan procfs in drop_pagecache_sb() Huang Shijie
2026-09-14  9:19   ` Alexey Dobriyan
2026-09-14 11:59     ` Huang Shijie
2026-09-14 12:10       ` Huang Shijie
2026-09-14  6:14 ` [RFC PATCH 2/3] fs/super: introduce a helper sb_inodes_empty() Huang Shijie
2026-09-14  6:14 ` [RFC PATCH 3/3] fs/proc: split the inode list Huang Shijie
2026-09-14  8:50 ` [syzbot ci] Re: fs/proc: split the inode list for procfs syzbot ci

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.