[<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