Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

nstree: make iterator generic

Move the namespace iteration infrastructure originally introduced for
mount namespaces into a generic library usable by all namespace types.

Signed-off-by: Christian Brauner <brauner@kernel.org>

+337 -1
+9
include/linux/ns_common.h
··· 3 3 #define _LINUX_NS_COMMON_H 4 4 5 5 #include <linux/refcount.h> 6 + #include <linux/rbtree.h> 6 7 7 8 struct proc_ns_operations; 8 9 ··· 21 20 const struct proc_ns_operations *ops; 22 21 unsigned int inum; 23 22 refcount_t count; 23 + union { 24 + struct { 25 + u64 ns_id; 26 + struct rb_node ns_tree_node; 27 + struct list_head ns_list_node; 28 + }; 29 + struct rcu_head ns_rcu; 30 + }; 24 31 }; 25 32 26 33 #define to_ns_common(__ns) \
+91
include/linux/nstree.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _LINUX_NSTREE_H 3 + #define _LINUX_NSTREE_H 4 + 5 + #include <linux/ns_common.h> 6 + #include <linux/nsproxy.h> 7 + #include <linux/rbtree.h> 8 + #include <linux/seqlock.h> 9 + #include <linux/rculist.h> 10 + #include <linux/cookie.h> 11 + 12 + /** 13 + * struct ns_tree - Namespace tree 14 + * @ns_tree: Rbtree of namespaces of a particular type 15 + * @ns_list: Sequentially walkable list of all namespaces of this type 16 + * @ns_tree_lock: Seqlock to protect the tree and list 17 + */ 18 + struct ns_tree { 19 + struct rb_root ns_tree; 20 + struct list_head ns_list; 21 + seqlock_t ns_tree_lock; 22 + int type; 23 + }; 24 + 25 + extern struct ns_tree cgroup_ns_tree; 26 + extern struct ns_tree ipc_ns_tree; 27 + extern struct ns_tree mnt_ns_tree; 28 + extern struct ns_tree net_ns_tree; 29 + extern struct ns_tree pid_ns_tree; 30 + extern struct ns_tree time_ns_tree; 31 + extern struct ns_tree user_ns_tree; 32 + extern struct ns_tree uts_ns_tree; 33 + 34 + #define to_ns_tree(__ns) \ 35 + _Generic((__ns), \ 36 + struct cgroup_namespace *: &(cgroup_ns_tree), \ 37 + struct ipc_namespace *: &(ipc_ns_tree), \ 38 + struct net *: &(net_ns_tree), \ 39 + struct pid_namespace *: &(pid_ns_tree), \ 40 + struct mnt_namespace *: &(mnt_ns_tree), \ 41 + struct time_namespace *: &(time_ns_tree), \ 42 + struct user_namespace *: &(user_ns_tree), \ 43 + struct uts_namespace *: &(uts_ns_tree)) 44 + 45 + u64 ns_tree_gen_id(struct ns_common *ns); 46 + void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree); 47 + void __ns_tree_remove(struct ns_common *ns, struct ns_tree *ns_tree); 48 + struct ns_common *ns_tree_lookup_rcu(u64 ns_id, int ns_type); 49 + struct ns_common *__ns_tree_adjoined_rcu(struct ns_common *ns, 50 + struct ns_tree *ns_tree, 51 + bool previous); 52 + 53 + static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree *ns_tree) 54 + { 55 + ns_tree_gen_id(ns); 56 + __ns_tree_add_raw(ns, ns_tree); 57 + } 58 + 59 + /** 60 + * ns_tree_add_raw - Add a namespace to a namespace 61 + * @ns: Namespace to add 62 + * 63 + * This function adds a namespace to the appropriate namespace tree 64 + * without assigning a id. 65 + */ 66 + #define ns_tree_add_raw(__ns) __ns_tree_add_raw(to_ns_common(__ns), to_ns_tree(__ns)) 67 + 68 + /** 69 + * ns_tree_add - Add a namespace to a namespace tree 70 + * @ns: Namespace to add 71 + * 72 + * This function assigns a new id to the namespace and adds it to the 73 + * appropriate namespace tree and list. 74 + */ 75 + #define ns_tree_add(__ns) __ns_tree_add(to_ns_common(__ns), to_ns_tree(__ns)) 76 + 77 + /** 78 + * ns_tree_remove - Remove a namespace from a namespace tree 79 + * @ns: Namespace to remove 80 + * 81 + * This function removes a namespace from the appropriate namespace 82 + * tree and list. 83 + */ 84 + #define ns_tree_remove(__ns) __ns_tree_remove(to_ns_common(__ns), to_ns_tree(__ns)) 85 + 86 + #define ns_tree_adjoined_rcu(__ns, __previous) \ 87 + __ns_tree_adjoined_rcu(to_ns_common(__ns), to_ns_tree(__ns), __previous) 88 + 89 + #define ns_tree_active(__ns) (!RB_EMPTY_NODE(&to_ns_common(__ns)->ns_tree_node)) 90 + 91 + #endif /* _LINUX_NSTREE_H */
+3
include/linux/proc_ns.h
··· 79 79 refcount_set(&ns->count, 1); 80 80 ns->stashed = NULL; 81 81 ns->ops = ops; 82 + ns->ns_id = 0; 83 + RB_CLEAR_NODE(&ns->ns_tree_node); 84 + INIT_LIST_HEAD(&ns->ns_list_node); 82 85 return 0; 83 86 } 84 87
+1 -1
kernel/Makefile
··· 8 8 sysctl.o capability.o ptrace.o user.o \ 9 9 signal.o sys.o umh.o workqueue.o pid.o task_work.o \ 10 10 extable.o params.o \ 11 - kthread.o sys_ni.o nsproxy.o \ 11 + kthread.o sys_ni.o nsproxy.o nstree.o \ 12 12 notifier.o ksysfs.o cred.o reboot.o \ 13 13 async.o range.o smpboot.o ucount.o regset.o ksyms_common.o 14 14
+233
kernel/nstree.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/nstree.h> 4 + #include <linux/proc_ns.h> 5 + #include <linux/vfsdebug.h> 6 + 7 + struct ns_tree mnt_ns_tree = { 8 + .ns_tree = RB_ROOT, 9 + .ns_list = LIST_HEAD_INIT(mnt_ns_tree.ns_list), 10 + .ns_tree_lock = __SEQLOCK_UNLOCKED(mnt_ns_tree.ns_tree_lock), 11 + .type = CLONE_NEWNS, 12 + }; 13 + 14 + struct ns_tree net_ns_tree = { 15 + .ns_tree = RB_ROOT, 16 + .ns_list = LIST_HEAD_INIT(net_ns_tree.ns_list), 17 + .ns_tree_lock = __SEQLOCK_UNLOCKED(net_ns_tree.ns_tree_lock), 18 + .type = CLONE_NEWNET, 19 + }; 20 + EXPORT_SYMBOL_GPL(net_ns_tree); 21 + 22 + struct ns_tree uts_ns_tree = { 23 + .ns_tree = RB_ROOT, 24 + .ns_list = LIST_HEAD_INIT(uts_ns_tree.ns_list), 25 + .ns_tree_lock = __SEQLOCK_UNLOCKED(uts_ns_tree.ns_tree_lock), 26 + .type = CLONE_NEWUTS, 27 + }; 28 + 29 + struct ns_tree user_ns_tree = { 30 + .ns_tree = RB_ROOT, 31 + .ns_list = LIST_HEAD_INIT(user_ns_tree.ns_list), 32 + .ns_tree_lock = __SEQLOCK_UNLOCKED(user_ns_tree.ns_tree_lock), 33 + .type = CLONE_NEWUSER, 34 + }; 35 + 36 + struct ns_tree ipc_ns_tree = { 37 + .ns_tree = RB_ROOT, 38 + .ns_list = LIST_HEAD_INIT(ipc_ns_tree.ns_list), 39 + .ns_tree_lock = __SEQLOCK_UNLOCKED(ipc_ns_tree.ns_tree_lock), 40 + .type = CLONE_NEWIPC, 41 + }; 42 + 43 + struct ns_tree pid_ns_tree = { 44 + .ns_tree = RB_ROOT, 45 + .ns_list = LIST_HEAD_INIT(pid_ns_tree.ns_list), 46 + .ns_tree_lock = __SEQLOCK_UNLOCKED(pid_ns_tree.ns_tree_lock), 47 + .type = CLONE_NEWPID, 48 + }; 49 + 50 + struct ns_tree cgroup_ns_tree = { 51 + .ns_tree = RB_ROOT, 52 + .ns_list = LIST_HEAD_INIT(cgroup_ns_tree.ns_list), 53 + .ns_tree_lock = __SEQLOCK_UNLOCKED(cgroup_ns_tree.ns_tree_lock), 54 + .type = CLONE_NEWCGROUP, 55 + }; 56 + 57 + struct ns_tree time_ns_tree = { 58 + .ns_tree = RB_ROOT, 59 + .ns_list = LIST_HEAD_INIT(time_ns_tree.ns_list), 60 + .ns_tree_lock = __SEQLOCK_UNLOCKED(time_ns_tree.ns_tree_lock), 61 + .type = CLONE_NEWTIME, 62 + }; 63 + 64 + DEFINE_COOKIE(namespace_cookie); 65 + 66 + static inline struct ns_common *node_to_ns(const struct rb_node *node) 67 + { 68 + if (!node) 69 + return NULL; 70 + return rb_entry(node, struct ns_common, ns_tree_node); 71 + } 72 + 73 + static inline int ns_cmp(struct rb_node *a, const struct rb_node *b) 74 + { 75 + struct ns_common *ns_a = node_to_ns(a); 76 + struct ns_common *ns_b = node_to_ns(b); 77 + u64 ns_id_a = ns_a->ns_id; 78 + u64 ns_id_b = ns_b->ns_id; 79 + 80 + if (ns_id_a < ns_id_b) 81 + return -1; 82 + if (ns_id_a > ns_id_b) 83 + return 1; 84 + return 0; 85 + } 86 + 87 + void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree) 88 + { 89 + struct rb_node *node, *prev; 90 + 91 + VFS_WARN_ON_ONCE(!ns->ns_id); 92 + 93 + write_seqlock(&ns_tree->ns_tree_lock); 94 + 95 + VFS_WARN_ON_ONCE(ns->ops->type != ns_tree->type); 96 + 97 + node = rb_find_add_rcu(&ns->ns_tree_node, &ns_tree->ns_tree, ns_cmp); 98 + /* 99 + * If there's no previous entry simply add it after the 100 + * head and if there is add it after the previous entry. 101 + */ 102 + prev = rb_prev(&ns->ns_tree_node); 103 + if (!prev) 104 + list_add_rcu(&ns->ns_list_node, &ns_tree->ns_list); 105 + else 106 + list_add_rcu(&ns->ns_list_node, &node_to_ns(prev)->ns_list_node); 107 + 108 + write_sequnlock(&ns_tree->ns_tree_lock); 109 + 110 + VFS_WARN_ON_ONCE(node); 111 + } 112 + 113 + void __ns_tree_remove(struct ns_common *ns, struct ns_tree *ns_tree) 114 + { 115 + VFS_WARN_ON_ONCE(RB_EMPTY_NODE(&ns->ns_tree_node)); 116 + VFS_WARN_ON_ONCE(list_empty(&ns->ns_list_node)); 117 + VFS_WARN_ON_ONCE(ns->ops->type != ns_tree->type); 118 + 119 + write_seqlock(&ns_tree->ns_tree_lock); 120 + rb_erase(&ns->ns_tree_node, &ns_tree->ns_tree); 121 + list_bidir_del_rcu(&ns->ns_list_node); 122 + RB_CLEAR_NODE(&ns->ns_tree_node); 123 + write_sequnlock(&ns_tree->ns_tree_lock); 124 + } 125 + EXPORT_SYMBOL_GPL(__ns_tree_remove); 126 + 127 + static int ns_find(const void *key, const struct rb_node *node) 128 + { 129 + const u64 ns_id = *(u64 *)key; 130 + const struct ns_common *ns = node_to_ns(node); 131 + 132 + if (ns_id < ns->ns_id) 133 + return -1; 134 + if (ns_id > ns->ns_id) 135 + return 1; 136 + return 0; 137 + } 138 + 139 + 140 + static struct ns_tree *ns_tree_from_type(int ns_type) 141 + { 142 + switch (ns_type) { 143 + case CLONE_NEWCGROUP: 144 + return &cgroup_ns_tree; 145 + case CLONE_NEWIPC: 146 + return &ipc_ns_tree; 147 + case CLONE_NEWNS: 148 + return &mnt_ns_tree; 149 + case CLONE_NEWNET: 150 + return &net_ns_tree; 151 + case CLONE_NEWPID: 152 + return &pid_ns_tree; 153 + case CLONE_NEWUSER: 154 + return &user_ns_tree; 155 + case CLONE_NEWUTS: 156 + return &uts_ns_tree; 157 + case CLONE_NEWTIME: 158 + return &time_ns_tree; 159 + } 160 + 161 + return NULL; 162 + } 163 + 164 + struct ns_common *ns_tree_lookup_rcu(u64 ns_id, int ns_type) 165 + { 166 + struct ns_tree *ns_tree; 167 + struct rb_node *node; 168 + unsigned int seq; 169 + 170 + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "suspicious ns_tree_lookup_rcu() usage"); 171 + 172 + ns_tree = ns_tree_from_type(ns_type); 173 + if (!ns_tree) 174 + return NULL; 175 + 176 + do { 177 + seq = read_seqbegin(&ns_tree->ns_tree_lock); 178 + node = rb_find_rcu(&ns_id, &ns_tree->ns_tree, ns_find); 179 + if (node) 180 + break; 181 + } while (read_seqretry(&ns_tree->ns_tree_lock, seq)); 182 + 183 + if (!node) 184 + return NULL; 185 + 186 + VFS_WARN_ON_ONCE(node_to_ns(node)->ops->type != ns_type); 187 + 188 + return node_to_ns(node); 189 + } 190 + 191 + /** 192 + * ns_tree_adjoined_rcu - find the next/previous namespace in the same 193 + * tree 194 + * @ns: namespace to start from 195 + * @previous: if true find the previous namespace, otherwise the next 196 + * 197 + * Find the next or previous namespace in the same tree as @ns. If 198 + * there is no next/previous namespace, -ENOENT is returned. 199 + */ 200 + struct ns_common *__ns_tree_adjoined_rcu(struct ns_common *ns, 201 + struct ns_tree *ns_tree, bool previous) 202 + { 203 + struct list_head *list; 204 + 205 + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "suspicious ns_tree_adjoined_rcu() usage"); 206 + 207 + if (previous) 208 + list = rcu_dereference(list_bidir_prev_rcu(&ns->ns_list_node)); 209 + else 210 + list = rcu_dereference(list_next_rcu(&ns->ns_list_node)); 211 + if (list_is_head(list, &ns_tree->ns_list)) 212 + return ERR_PTR(-ENOENT); 213 + 214 + VFS_WARN_ON_ONCE(list_entry_rcu(list, struct ns_common, ns_list_node)->ops->type != ns_tree->type); 215 + 216 + return list_entry_rcu(list, struct ns_common, ns_list_node); 217 + } 218 + 219 + /** 220 + * ns_tree_gen_id - generate a new namespace id 221 + * @ns: namespace to generate id for 222 + * 223 + * Generates a new namespace id and assigns it to the namespace. All 224 + * namespaces types share the same id space and thus can be compared 225 + * directly. IOW, when two ids of two namespace are equal, they are 226 + * identical. 227 + */ 228 + u64 ns_tree_gen_id(struct ns_common *ns) 229 + { 230 + guard(preempt)(); 231 + ns->ns_id = gen_cookie_next(&namespace_cookie); 232 + return ns->ns_id; 233 + }