lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250411183109.6334-4-alexjlzheng@tencent.com>
Date: Sat, 12 Apr 2025 02:31:09 +0800
From: alexjlzheng@...il.com
To: gregkh@...uxfoundation.org,
	tj@...nel.org
Cc: alexjlzheng@...cent.com,
	linux-kernel@...r.kernel.org
Subject: [PATCH kernfs 3/3] kernfs: switch global kernfs_pr_cont_lock to per-fs lock

From: Jinliang Zheng <alexjlzheng@...cent.com>

The kernfs implementation has big lock granularity(kernfs_pr_cont_lock) so
every kernfs-based(e.g., sysfs, cgroup) fs are able to compete the lock.

This patch switches the global kernfs_pr_cont_lock to per-fs lock, which
put the spinlock into kernfs_root. Of course, kernfs_pr_cont_buf also needs
to be moved to kernfs_root.

Signed-off-by: Jinliang Zheng <alexjlzheng@...cent.com>
---
 fs/kernfs/dir.c             | 46 +++++++++++++++++--------------------
 fs/kernfs/kernfs-internal.h | 11 +++++++++
 2 files changed, 32 insertions(+), 25 deletions(-)

diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index d63a96786c9b..29605d7f0ab0 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -17,16 +17,6 @@
 
 #include "kernfs-internal.h"
 
-/*
- * Don't use rename_lock to piggy back on pr_cont_buf. We don't want to
- * call pr_cont() while holding rename_lock. Because sometimes pr_cont()
- * will perform wakeups when releasing console_sem. Holding rename_lock
- * will introduce deadlock if the scheduler reads the kernfs_name in the
- * wakeup path.
- */
-static DEFINE_SPINLOCK(kernfs_pr_cont_lock);
-static char kernfs_pr_cont_buf[PATH_MAX];	/* protected by pr_cont_lock */
-
 #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb)
 
 static bool __kernfs_active(struct kernfs_node *kn)
@@ -244,13 +234,15 @@ EXPORT_SYMBOL_GPL(kernfs_path_from_node);
 void pr_cont_kernfs_name(struct kernfs_node *kn)
 {
 	unsigned long flags;
+	struct kernfs_root *root = kernfs_root(kn);
 
-	spin_lock_irqsave(&kernfs_pr_cont_lock, flags);
+	spin_lock_irqsave(&root->kernfs_pr_cont_lock, flags);
 
-	kernfs_name(kn, kernfs_pr_cont_buf, sizeof(kernfs_pr_cont_buf));
-	pr_cont("%s", kernfs_pr_cont_buf);
+	kernfs_name(kn, root->kernfs_pr_cont_buf,
+			sizeof(root->kernfs_pr_cont_buf));
+	pr_cont("%s", root->kernfs_pr_cont_buf);
 
-	spin_unlock_irqrestore(&kernfs_pr_cont_lock, flags);
+	spin_unlock_irqrestore(&root->kernfs_pr_cont_lock, flags);
 }
 
 /**
@@ -263,11 +255,12 @@ void pr_cont_kernfs_path(struct kernfs_node *kn)
 {
 	unsigned long flags;
 	int sz;
+	struct kernfs_root *root = kernfs_root(kn);
 
-	spin_lock_irqsave(&kernfs_pr_cont_lock, flags);
+	spin_lock_irqsave(&root->kernfs_pr_cont_lock, flags);
 
-	sz = kernfs_path_from_node(kn, NULL, kernfs_pr_cont_buf,
-				   sizeof(kernfs_pr_cont_buf));
+	sz = kernfs_path_from_node(kn, NULL, root->kernfs_pr_cont_buf,
+				   sizeof(root->kernfs_pr_cont_buf));
 	if (sz < 0) {
 		if (sz == -E2BIG)
 			pr_cont("(name too long)");
@@ -276,10 +269,10 @@ void pr_cont_kernfs_path(struct kernfs_node *kn)
 		goto out;
 	}
 
-	pr_cont("%s", kernfs_pr_cont_buf);
+	pr_cont("%s", root->kernfs_pr_cont_buf);
 
 out:
-	spin_unlock_irqrestore(&kernfs_pr_cont_lock, flags);
+	spin_unlock_irqrestore(&root->kernfs_pr_cont_lock, flags);
 }
 
 /**
@@ -888,19 +881,21 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
 {
 	ssize_t len;
 	char *p, *name;
+	struct kernfs_root *root = kernfs_root(parent);
 
-	lockdep_assert_held_read(&kernfs_root(parent)->kernfs_rwsem);
+	lockdep_assert_held_read(&root->kernfs_rwsem);
 
-	spin_lock_irq(&kernfs_pr_cont_lock);
+	spin_lock_irq(&root->kernfs_pr_cont_lock);
 
-	len = strscpy(kernfs_pr_cont_buf, path, sizeof(kernfs_pr_cont_buf));
+	len = strscpy(root->kernfs_pr_cont_buf, path,
+			sizeof(root->kernfs_pr_cont_buf));
 
 	if (len < 0) {
-		spin_unlock_irq(&kernfs_pr_cont_lock);
+		spin_unlock_irq(&root->kernfs_pr_cont_lock);
 		return NULL;
 	}
 
-	p = kernfs_pr_cont_buf;
+	p = root->kernfs_pr_cont_buf;
 
 	while ((name = strsep(&p, "/")) && parent) {
 		if (*name == '\0')
@@ -908,7 +903,7 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
 		parent = kernfs_find_ns(parent, name, ns);
 	}
 
-	spin_unlock_irq(&kernfs_pr_cont_lock);
+	spin_unlock_irq(&root->kernfs_pr_cont_lock);
 
 	return parent;
 }
@@ -995,6 +990,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
 	init_rwsem(&root->kernfs_supers_rwsem);
 	INIT_LIST_HEAD(&root->supers);
 	rwlock_init(&root->kernfs_rename_lock);
+	spin_lock_init(&root->kernfs_pr_cont_lock);
 
 	/*
 	 * On 64bit ino setups, id is ino.  On 32bit, low 32bits are ino.
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index 6061b6f70d2a..c7fe50955e8c 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -55,6 +55,17 @@ struct kernfs_root {
 	rwlock_t		kernfs_rename_lock;
 
 	struct rcu_head		rcu;
+
+	/*
+	 * Don't use rename_lock to piggy back on pr_cont_buf. We don't want to
+	 * call pr_cont() while holding rename_lock. Because sometimes pr_cont()
+	 * will perform wakeups when releasing console_sem. Holding rename_lock
+	 * will introduce deadlock if the scheduler reads the kernfs_name in the
+	 * wakeup path.
+	 */
+	spinlock_t		kernfs_pr_cont_lock;
+	/* protected by pr_cont_lock */
+	char			kernfs_pr_cont_buf[PATH_MAX];
 };
 
 /* +1 to avoid triggering overflow warning when negating it */
-- 
2.48.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ