cgroup only renames nodes and keeps the same parent node. It can be switched RCU for name lookups to avoid acquiring a lock. For the switch the flag KERNFS_ROOT_SAME_PARENT is added while creating the root node and lookups are switched to the _rcu() interface. The pr_cont_kernfs_.*() is only used by cgroup and is renamed as part of the switch. kernfs_path() has one user in tree, sysfs. kernfs_name() has no callers, could be removed. kernfs_path_from_node() has no in-tree module callers, module export is removed. Fixes: 2b5067a8143e3 ("mm: mmap_lock: add tracepoints around lock acquisition") Reported-by: syzbot+6ea37e2e6ffccf41a7e6@xxxxxxxxxxxxxxxxxxxxxxxxx Closes: https://lore.kernel.org/lkml/67251dc6.050a0220.529b6.015e.GAE@xxxxxxxxxx/ Reported-by: Hillf Danton <hdanton@xxxxxxxx> Closes: https://lore.kernel.org/20241102001224.2789-1-hdanton@xxxxxxxx Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- fs/kernfs/dir.c | 21 +++++++++++---------- include/linux/cgroup.h | 8 ++++---- include/linux/kernfs.h | 26 ++++++++++++++++++++++---- kernel/cgroup/cgroup.c | 9 +++++---- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 41c87ee76aa70..93bdaad8b9886 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -264,7 +264,6 @@ int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from, } return kernfs_path_from_node_locked(to, from, buf, buflen); } -EXPORT_SYMBOL_GPL(kernfs_path_from_node); /** * kernfs_path_from_node_rcu - build path of node @to relative to @from. @@ -291,38 +290,40 @@ int kernfs_path_from_node_rcu(struct kernfs_node *to, struct kernfs_node *from, } /** - * pr_cont_kernfs_name - pr_cont name of a kernfs_node + * pr_cont_kernfs_name_rcu - pr_cont name of a kernfs_node * @kn: kernfs_node of interest * - * This function can be called from any context. + * This function can be called from any context. The root node must be with + * KERNFS_ROOT_SAME_PARENT. */ -void pr_cont_kernfs_name(struct kernfs_node *kn) +void pr_cont_kernfs_name_rcu(struct kernfs_node *kn) { unsigned long flags; spin_lock_irqsave(&kernfs_pr_cont_lock, flags); - kernfs_name(kn, kernfs_pr_cont_buf, sizeof(kernfs_pr_cont_buf)); + kernfs_name_rcu(kn, kernfs_pr_cont_buf, sizeof(kernfs_pr_cont_buf)); pr_cont("%s", kernfs_pr_cont_buf); spin_unlock_irqrestore(&kernfs_pr_cont_lock, flags); } /** - * pr_cont_kernfs_path - pr_cont path of a kernfs_node + * pr_cont_kernfs_path_rcu - pr_cont path of a kernfs_node * @kn: kernfs_node of interest * - * This function can be called from any context. + * This function can be called from any context. The root node must be with + * KERNFS_ROOT_SAME_PARENT. */ -void pr_cont_kernfs_path(struct kernfs_node *kn) +void pr_cont_kernfs_path_rcu(struct kernfs_node *kn) { unsigned long flags; int sz; spin_lock_irqsave(&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_rcu(kn, NULL, kernfs_pr_cont_buf, + sizeof(kernfs_pr_cont_buf)); if (sz < 0) { if (sz == -E2BIG) pr_cont("(name too long)"); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f8ef47f8a634d..555a299e583ef 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -591,22 +591,22 @@ static inline struct cgroup_subsys_state *seq_css(struct seq_file *seq) static inline int cgroup_name(struct cgroup *cgrp, char *buf, size_t buflen) { - return kernfs_name(cgrp->kn, buf, buflen); + return kernfs_name_rcu(cgrp->kn, buf, buflen); } static inline int cgroup_path(struct cgroup *cgrp, char *buf, size_t buflen) { - return kernfs_path(cgrp->kn, buf, buflen); + return kernfs_path_rcu(cgrp->kn, buf, buflen); } static inline void pr_cont_cgroup_name(struct cgroup *cgrp) { - pr_cont_kernfs_name(cgrp->kn); + pr_cont_kernfs_name_rcu(cgrp->kn); } static inline void pr_cont_cgroup_path(struct cgroup *cgrp) { - pr_cont_kernfs_path(cgrp->kn); + pr_cont_kernfs_path_rcu(cgrp->kn); } bool cgroup_psi_enabled(void); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index b52393f1045c6..a1907f3c944d0 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -408,8 +408,8 @@ int kernfs_path_from_node(struct kernfs_node *kn_to, struct kernfs_node *kn_from char *buf, size_t buflen); int kernfs_path_from_node_rcu(struct kernfs_node *kn_to, struct kernfs_node *kn_from, char *buf, size_t buflen); -void pr_cont_kernfs_name(struct kernfs_node *kn); -void pr_cont_kernfs_path(struct kernfs_node *kn); +void pr_cont_kernfs_name_rcu(struct kernfs_node *kn); +void pr_cont_kernfs_path_rcu(struct kernfs_node *kn); struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn); struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, const void *ns); @@ -499,8 +499,8 @@ static inline int kernfs_path_from_node_rcu(struct kernfs_node *root_kn, char *buf, size_t buflen) { return -ENOSYS; } -static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { } -static inline void pr_cont_kernfs_path(struct kernfs_node *kn) { } +static inline void pr_cont_kernfs_name_rcu(struct kernfs_node *kn) { } +static inline void pr_cont_kernfs_path_rcu(struct kernfs_node *kn) { } static inline struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn) { return NULL; } @@ -617,6 +617,24 @@ static inline int kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen) return kernfs_path_from_node(kn, NULL, buf, buflen); } +/** + * kernfs_path_rcu - build full path of a given node + * @kn: kernfs_node of interest + * @buf: buffer to copy @kn's name into + * @buflen: size of @buf + * + * If @kn is NULL result will be "(null)". The root node must be with + * KERNFS_ROOT_SAME_PARENT. + * + * Returns the length of the full path. If the full length is equal to or + * greater than @buflen, @buf contains the truncated path with the trailing + * '\0'. On error, -errno is returned. + */ +static inline int kernfs_path_rcu(struct kernfs_node *kn, char *buf, size_t buflen) +{ + return kernfs_path_from_node_rcu(kn, NULL, buf, buflen); +} + static inline struct kernfs_node * kernfs_find_and_get(struct kernfs_node *kn, const char *name) { diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 044c7ba1cc482..6f8d555529525 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -1906,7 +1906,7 @@ int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node, spin_lock_irq(&css_set_lock); ns_cgroup = current_cgns_cgroup_from_root(kf_cgroot); - len = kernfs_path_from_node(kf_node, ns_cgroup->kn, buf, PATH_MAX); + len = kernfs_path_from_node_rcu(kf_node, ns_cgroup->kn, buf, PATH_MAX); spin_unlock_irq(&css_set_lock); if (len == -E2BIG) @@ -2118,7 +2118,8 @@ int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask) root->kf_root = kernfs_create_root(kf_sops, KERNFS_ROOT_CREATE_DEACTIVATED | KERNFS_ROOT_SUPPORT_EXPORTOP | - KERNFS_ROOT_SUPPORT_USER_XATTR, + KERNFS_ROOT_SUPPORT_USER_XATTR | + KERNFS_ROOT_SAME_PARENT, root_cgrp); if (IS_ERR(root->kf_root)) { ret = PTR_ERR(root->kf_root); @@ -2387,7 +2388,7 @@ int cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen, { struct cgroup *root = cset_cgroup_from_root(ns->root_cset, cgrp->root); - return kernfs_path_from_node(cgrp->kn, root->kn, buf, buflen); + return kernfs_path_from_node_rcu(cgrp->kn, root->kn, buf, buflen); } int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen, @@ -6275,7 +6276,7 @@ void cgroup_path_from_kernfs_id(u64 id, char *buf, size_t buflen) kn = kernfs_find_and_get_node_by_id(cgrp_dfl_root.kf_root, id); if (!kn) return; - kernfs_path(kn, buf, buflen); + kernfs_path_rcu(kn, buf, buflen); kernfs_put(kn); } -- 2.45.2