mirror of https://lore.kernel.org/lkml/
 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	[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	[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	[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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®