]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
pidfs: adapt to rhashtable-based simple_xattrs
authorChristian Brauner <brauner@kernel.org>
Mon, 16 Feb 2026 13:32:01 +0000 (14:32 +0100)
committerChristian Brauner <brauner@kernel.org>
Mon, 2 Mar 2026 10:06:41 +0000 (11:06 +0100)
Adapt pidfs to use the rhashtable-based xattr path by switching from a
dedicated slab cache to simple_xattrs_alloc().

Previously pidfs used a custom kmem_cache (pidfs_xattr_cachep) that
allocated a struct containing an embedded simple_xattrs plus
simple_xattrs_init(). Replace this with simple_xattrs_alloc() which
combines kzalloc + rhashtable_init, and drop the dedicated slab cache
entirely.

Use simple_xattr_free_rcu() for replaced xattr entries to allow
concurrent RCU readers to finish.

Link: https://patch.msgid.link/20260216-work-xattr-socket-v1-5-c2efa4f74cb7@kernel.org
Acked-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/pidfs.c

index 318253344b5c5b2575c975ee16e7967d7703fc81..bae8e5d32a1cd39a7a4a33605365c7ee926100e2 100644 (file)
@@ -22,6 +22,7 @@
 #include <net/net_namespace.h>
 #include <linux/coredump.h>
 #include <linux/rhashtable.h>
+#include <linux/llist.h>
 #include <linux/xattr.h>
 #include <linux/cookie.h>
 
@@ -31,7 +32,6 @@
 #define PIDFS_PID_DEAD ERR_PTR(-ESRCH)
 
 static struct kmem_cache *pidfs_attr_cachep __ro_after_init;
-static struct kmem_cache *pidfs_xattr_cachep __ro_after_init;
 
 static struct path pidfs_root_path = {};
 
@@ -46,9 +46,8 @@ enum pidfs_attr_mask_bits {
        PIDFS_ATTR_BIT_COREDUMP = 1,
 };
 
-struct pidfs_attr {
+struct pidfs_anon_attr {
        unsigned long attr_mask;
-       struct simple_xattrs *xattrs;
        struct /* exit info */ {
                __u64 cgroupid;
                __s32 exit_code;
@@ -93,6 +92,13 @@ static const struct rhashtable_params pidfs_ino_ht_params = {
  * inode number and the inode generation number to compare or
  * use file handles.
  */
+struct pidfs_attr {
+       struct simple_xattrs *xattrs;
+       union {
+               struct pidfs_anon_attr;
+               struct llist_node pidfs_llist;
+       };
+};
 
 #if BITS_PER_LONG == 32
 
@@ -178,10 +184,30 @@ void pidfs_remove_pid(struct pid *pid)
                                       pidfs_ino_ht_params);
 }
 
+static LLIST_HEAD(pidfs_free_list);
+
+static void pidfs_free_attr_work(struct work_struct *work)
+{
+       struct pidfs_attr *attr, *next;
+       struct llist_node *head;
+
+       head = llist_del_all(&pidfs_free_list);
+       llist_for_each_entry_safe(attr, next, head, pidfs_llist) {
+               struct simple_xattrs *xattrs = attr->xattrs;
+
+               if (xattrs) {
+                       simple_xattrs_free(xattrs, NULL);
+                       kfree(xattrs);
+               }
+               kfree(attr);
+       }
+}
+
+static DECLARE_WORK(pidfs_free_work, pidfs_free_attr_work);
+
 void pidfs_free_pid(struct pid *pid)
 {
-       struct pidfs_attr *attr __free(kfree) = no_free_ptr(pid->attr);
-       struct simple_xattrs *xattrs __free(kfree) = NULL;
+       struct pidfs_attr *attr = pid->attr;
 
        /*
         * Any dentry must've been wiped from the pid by now.
@@ -200,9 +226,10 @@ void pidfs_free_pid(struct pid *pid)
        if (IS_ERR(attr))
                return;
 
-       xattrs = no_free_ptr(attr->xattrs);
-       if (xattrs)
-               simple_xattrs_free(xattrs, NULL);
+       if (likely(!attr->xattrs))
+               kfree(attr);
+       else if (llist_add(&attr->pidfs_llist, &pidfs_free_list))
+               schedule_work(&pidfs_free_work);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -1011,7 +1038,7 @@ static int pidfs_xattr_get(const struct xattr_handler *handler,
 
        xattrs = READ_ONCE(attr->xattrs);
        if (!xattrs)
-               return 0;
+               return -ENODATA;
 
        name = xattr_full_name(handler, suffix);
        return simple_xattr_get(xattrs, name, value, size);
@@ -1031,22 +1058,16 @@ static int pidfs_xattr_set(const struct xattr_handler *handler,
        /* Ensure we're the only one to set @attr->xattrs. */
        WARN_ON_ONCE(!inode_is_locked(inode));
 
-       xattrs = READ_ONCE(attr->xattrs);
-       if (!xattrs) {
-               xattrs = kmem_cache_zalloc(pidfs_xattr_cachep, GFP_KERNEL);
-               if (!xattrs)
-                       return -ENOMEM;
-
-               simple_xattrs_init(xattrs);
-               smp_store_release(&pid->attr->xattrs, xattrs);
-       }
+       xattrs = simple_xattrs_lazy_alloc(&attr->xattrs, value, flags);
+       if (IS_ERR_OR_NULL(xattrs))
+               return PTR_ERR(xattrs);
 
        name = xattr_full_name(handler, suffix);
        old_xattr = simple_xattr_set(xattrs, name, value, size, flags);
        if (IS_ERR(old_xattr))
                return PTR_ERR(old_xattr);
 
-       simple_xattr_free(old_xattr);
+       simple_xattr_free_rcu(old_xattr);
        return 0;
 }
 
@@ -1124,11 +1145,6 @@ void __init pidfs_init(void)
                                         (SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT |
                                          SLAB_ACCOUNT | SLAB_PANIC), NULL);
 
-       pidfs_xattr_cachep = kmem_cache_create("pidfs_xattr_cache",
-                                              sizeof(struct simple_xattrs), 0,
-                                              (SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT |
-                                               SLAB_ACCOUNT | SLAB_PANIC), NULL);
-
        pidfs_mnt = kern_mount(&pidfs_type);
        if (IS_ERR(pidfs_mnt))
                panic("Failed to mount pidfs pseudo filesystem");