1885fc8acSChristian Brauner // SPDX-License-Identifier: GPL-2.0-only 2885fc8acSChristian Brauner 3885fc8acSChristian Brauner #include <linux/nstree.h> 4885fc8acSChristian Brauner #include <linux/proc_ns.h> 5885fc8acSChristian Brauner #include <linux/vfsdebug.h> 6885fc8acSChristian Brauner 7*10cdfcd3SChristian Brauner /** 8*10cdfcd3SChristian Brauner * struct ns_tree - Namespace tree 9*10cdfcd3SChristian Brauner * @ns_tree: Rbtree of namespaces of a particular type 10*10cdfcd3SChristian Brauner * @ns_list: Sequentially walkable list of all namespaces of this type 11*10cdfcd3SChristian Brauner * @ns_tree_lock: Seqlock to protect the tree and list 12*10cdfcd3SChristian Brauner * @type: type of namespaces in this tree 13*10cdfcd3SChristian Brauner */ 14*10cdfcd3SChristian Brauner struct ns_tree { 15*10cdfcd3SChristian Brauner struct rb_root ns_tree; 16*10cdfcd3SChristian Brauner struct list_head ns_list; 17*10cdfcd3SChristian Brauner seqlock_t ns_tree_lock; 18*10cdfcd3SChristian Brauner int type; 19*10cdfcd3SChristian Brauner }; 20*10cdfcd3SChristian Brauner 21885fc8acSChristian Brauner struct ns_tree mnt_ns_tree = { 22885fc8acSChristian Brauner .ns_tree = RB_ROOT, 23885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(mnt_ns_tree.ns_list), 24885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(mnt_ns_tree.ns_tree_lock), 25885fc8acSChristian Brauner .type = CLONE_NEWNS, 26885fc8acSChristian Brauner }; 27885fc8acSChristian Brauner 28885fc8acSChristian Brauner struct ns_tree net_ns_tree = { 29885fc8acSChristian Brauner .ns_tree = RB_ROOT, 30885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(net_ns_tree.ns_list), 31885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(net_ns_tree.ns_tree_lock), 32885fc8acSChristian Brauner .type = CLONE_NEWNET, 33885fc8acSChristian Brauner }; 34885fc8acSChristian Brauner EXPORT_SYMBOL_GPL(net_ns_tree); 35885fc8acSChristian Brauner 36885fc8acSChristian Brauner struct ns_tree uts_ns_tree = { 37885fc8acSChristian Brauner .ns_tree = RB_ROOT, 38885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(uts_ns_tree.ns_list), 39885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(uts_ns_tree.ns_tree_lock), 40885fc8acSChristian Brauner .type = CLONE_NEWUTS, 41885fc8acSChristian Brauner }; 42885fc8acSChristian Brauner 43885fc8acSChristian Brauner struct ns_tree user_ns_tree = { 44885fc8acSChristian Brauner .ns_tree = RB_ROOT, 45885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(user_ns_tree.ns_list), 46885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(user_ns_tree.ns_tree_lock), 47885fc8acSChristian Brauner .type = CLONE_NEWUSER, 48885fc8acSChristian Brauner }; 49885fc8acSChristian Brauner 50885fc8acSChristian Brauner struct ns_tree ipc_ns_tree = { 51885fc8acSChristian Brauner .ns_tree = RB_ROOT, 52885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(ipc_ns_tree.ns_list), 53885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(ipc_ns_tree.ns_tree_lock), 54885fc8acSChristian Brauner .type = CLONE_NEWIPC, 55885fc8acSChristian Brauner }; 56885fc8acSChristian Brauner 57885fc8acSChristian Brauner struct ns_tree pid_ns_tree = { 58885fc8acSChristian Brauner .ns_tree = RB_ROOT, 59885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(pid_ns_tree.ns_list), 60885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(pid_ns_tree.ns_tree_lock), 61885fc8acSChristian Brauner .type = CLONE_NEWPID, 62885fc8acSChristian Brauner }; 63885fc8acSChristian Brauner 64885fc8acSChristian Brauner struct ns_tree cgroup_ns_tree = { 65885fc8acSChristian Brauner .ns_tree = RB_ROOT, 66885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(cgroup_ns_tree.ns_list), 67885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(cgroup_ns_tree.ns_tree_lock), 68885fc8acSChristian Brauner .type = CLONE_NEWCGROUP, 69885fc8acSChristian Brauner }; 70885fc8acSChristian Brauner 71885fc8acSChristian Brauner struct ns_tree time_ns_tree = { 72885fc8acSChristian Brauner .ns_tree = RB_ROOT, 73885fc8acSChristian Brauner .ns_list = LIST_HEAD_INIT(time_ns_tree.ns_list), 74885fc8acSChristian Brauner .ns_tree_lock = __SEQLOCK_UNLOCKED(time_ns_tree.ns_tree_lock), 75885fc8acSChristian Brauner .type = CLONE_NEWTIME, 76885fc8acSChristian Brauner }; 77885fc8acSChristian Brauner 78885fc8acSChristian Brauner DEFINE_COOKIE(namespace_cookie); 79885fc8acSChristian Brauner 80885fc8acSChristian Brauner static inline struct ns_common *node_to_ns(const struct rb_node *node) 81885fc8acSChristian Brauner { 82885fc8acSChristian Brauner if (!node) 83885fc8acSChristian Brauner return NULL; 84885fc8acSChristian Brauner return rb_entry(node, struct ns_common, ns_tree_node); 85885fc8acSChristian Brauner } 86885fc8acSChristian Brauner 87885fc8acSChristian Brauner static inline int ns_cmp(struct rb_node *a, const struct rb_node *b) 88885fc8acSChristian Brauner { 89885fc8acSChristian Brauner struct ns_common *ns_a = node_to_ns(a); 90885fc8acSChristian Brauner struct ns_common *ns_b = node_to_ns(b); 91885fc8acSChristian Brauner u64 ns_id_a = ns_a->ns_id; 92885fc8acSChristian Brauner u64 ns_id_b = ns_b->ns_id; 93885fc8acSChristian Brauner 94885fc8acSChristian Brauner if (ns_id_a < ns_id_b) 95885fc8acSChristian Brauner return -1; 96885fc8acSChristian Brauner if (ns_id_a > ns_id_b) 97885fc8acSChristian Brauner return 1; 98885fc8acSChristian Brauner return 0; 99885fc8acSChristian Brauner } 100885fc8acSChristian Brauner 101885fc8acSChristian Brauner void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree) 102885fc8acSChristian Brauner { 103885fc8acSChristian Brauner struct rb_node *node, *prev; 104885fc8acSChristian Brauner 105885fc8acSChristian Brauner VFS_WARN_ON_ONCE(!ns->ns_id); 106885fc8acSChristian Brauner 107885fc8acSChristian Brauner write_seqlock(&ns_tree->ns_tree_lock); 108885fc8acSChristian Brauner 109885fc8acSChristian Brauner VFS_WARN_ON_ONCE(ns->ops->type != ns_tree->type); 110885fc8acSChristian Brauner 111885fc8acSChristian Brauner node = rb_find_add_rcu(&ns->ns_tree_node, &ns_tree->ns_tree, ns_cmp); 112885fc8acSChristian Brauner /* 113885fc8acSChristian Brauner * If there's no previous entry simply add it after the 114885fc8acSChristian Brauner * head and if there is add it after the previous entry. 115885fc8acSChristian Brauner */ 116885fc8acSChristian Brauner prev = rb_prev(&ns->ns_tree_node); 117885fc8acSChristian Brauner if (!prev) 118885fc8acSChristian Brauner list_add_rcu(&ns->ns_list_node, &ns_tree->ns_list); 119885fc8acSChristian Brauner else 120885fc8acSChristian Brauner list_add_rcu(&ns->ns_list_node, &node_to_ns(prev)->ns_list_node); 121885fc8acSChristian Brauner 122885fc8acSChristian Brauner write_sequnlock(&ns_tree->ns_tree_lock); 123885fc8acSChristian Brauner 124885fc8acSChristian Brauner VFS_WARN_ON_ONCE(node); 125885fc8acSChristian Brauner } 126885fc8acSChristian Brauner 127885fc8acSChristian Brauner void __ns_tree_remove(struct ns_common *ns, struct ns_tree *ns_tree) 128885fc8acSChristian Brauner { 129885fc8acSChristian Brauner VFS_WARN_ON_ONCE(RB_EMPTY_NODE(&ns->ns_tree_node)); 130885fc8acSChristian Brauner VFS_WARN_ON_ONCE(list_empty(&ns->ns_list_node)); 131885fc8acSChristian Brauner VFS_WARN_ON_ONCE(ns->ops->type != ns_tree->type); 132885fc8acSChristian Brauner 133885fc8acSChristian Brauner write_seqlock(&ns_tree->ns_tree_lock); 134885fc8acSChristian Brauner rb_erase(&ns->ns_tree_node, &ns_tree->ns_tree); 135885fc8acSChristian Brauner list_bidir_del_rcu(&ns->ns_list_node); 136885fc8acSChristian Brauner RB_CLEAR_NODE(&ns->ns_tree_node); 137885fc8acSChristian Brauner write_sequnlock(&ns_tree->ns_tree_lock); 138885fc8acSChristian Brauner } 139885fc8acSChristian Brauner EXPORT_SYMBOL_GPL(__ns_tree_remove); 140885fc8acSChristian Brauner 141885fc8acSChristian Brauner static int ns_find(const void *key, const struct rb_node *node) 142885fc8acSChristian Brauner { 143885fc8acSChristian Brauner const u64 ns_id = *(u64 *)key; 144885fc8acSChristian Brauner const struct ns_common *ns = node_to_ns(node); 145885fc8acSChristian Brauner 146885fc8acSChristian Brauner if (ns_id < ns->ns_id) 147885fc8acSChristian Brauner return -1; 148885fc8acSChristian Brauner if (ns_id > ns->ns_id) 149885fc8acSChristian Brauner return 1; 150885fc8acSChristian Brauner return 0; 151885fc8acSChristian Brauner } 152885fc8acSChristian Brauner 153885fc8acSChristian Brauner 154885fc8acSChristian Brauner static struct ns_tree *ns_tree_from_type(int ns_type) 155885fc8acSChristian Brauner { 156885fc8acSChristian Brauner switch (ns_type) { 157885fc8acSChristian Brauner case CLONE_NEWCGROUP: 158885fc8acSChristian Brauner return &cgroup_ns_tree; 159885fc8acSChristian Brauner case CLONE_NEWIPC: 160885fc8acSChristian Brauner return &ipc_ns_tree; 161885fc8acSChristian Brauner case CLONE_NEWNS: 162885fc8acSChristian Brauner return &mnt_ns_tree; 163885fc8acSChristian Brauner case CLONE_NEWNET: 164885fc8acSChristian Brauner return &net_ns_tree; 165885fc8acSChristian Brauner case CLONE_NEWPID: 166885fc8acSChristian Brauner return &pid_ns_tree; 167885fc8acSChristian Brauner case CLONE_NEWUSER: 168885fc8acSChristian Brauner return &user_ns_tree; 169885fc8acSChristian Brauner case CLONE_NEWUTS: 170885fc8acSChristian Brauner return &uts_ns_tree; 171885fc8acSChristian Brauner case CLONE_NEWTIME: 172885fc8acSChristian Brauner return &time_ns_tree; 173885fc8acSChristian Brauner } 174885fc8acSChristian Brauner 175885fc8acSChristian Brauner return NULL; 176885fc8acSChristian Brauner } 177885fc8acSChristian Brauner 178885fc8acSChristian Brauner struct ns_common *ns_tree_lookup_rcu(u64 ns_id, int ns_type) 179885fc8acSChristian Brauner { 180885fc8acSChristian Brauner struct ns_tree *ns_tree; 181885fc8acSChristian Brauner struct rb_node *node; 182885fc8acSChristian Brauner unsigned int seq; 183885fc8acSChristian Brauner 184885fc8acSChristian Brauner RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "suspicious ns_tree_lookup_rcu() usage"); 185885fc8acSChristian Brauner 186885fc8acSChristian Brauner ns_tree = ns_tree_from_type(ns_type); 187885fc8acSChristian Brauner if (!ns_tree) 188885fc8acSChristian Brauner return NULL; 189885fc8acSChristian Brauner 190885fc8acSChristian Brauner do { 191885fc8acSChristian Brauner seq = read_seqbegin(&ns_tree->ns_tree_lock); 192885fc8acSChristian Brauner node = rb_find_rcu(&ns_id, &ns_tree->ns_tree, ns_find); 193885fc8acSChristian Brauner if (node) 194885fc8acSChristian Brauner break; 195885fc8acSChristian Brauner } while (read_seqretry(&ns_tree->ns_tree_lock, seq)); 196885fc8acSChristian Brauner 197885fc8acSChristian Brauner if (!node) 198885fc8acSChristian Brauner return NULL; 199885fc8acSChristian Brauner 200885fc8acSChristian Brauner VFS_WARN_ON_ONCE(node_to_ns(node)->ops->type != ns_type); 201885fc8acSChristian Brauner 202885fc8acSChristian Brauner return node_to_ns(node); 203885fc8acSChristian Brauner } 204885fc8acSChristian Brauner 205885fc8acSChristian Brauner /** 206885fc8acSChristian Brauner * ns_tree_adjoined_rcu - find the next/previous namespace in the same 207885fc8acSChristian Brauner * tree 208885fc8acSChristian Brauner * @ns: namespace to start from 209885fc8acSChristian Brauner * @previous: if true find the previous namespace, otherwise the next 210885fc8acSChristian Brauner * 211885fc8acSChristian Brauner * Find the next or previous namespace in the same tree as @ns. If 212885fc8acSChristian Brauner * there is no next/previous namespace, -ENOENT is returned. 213885fc8acSChristian Brauner */ 214885fc8acSChristian Brauner struct ns_common *__ns_tree_adjoined_rcu(struct ns_common *ns, 215885fc8acSChristian Brauner struct ns_tree *ns_tree, bool previous) 216885fc8acSChristian Brauner { 217885fc8acSChristian Brauner struct list_head *list; 218885fc8acSChristian Brauner 219885fc8acSChristian Brauner RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "suspicious ns_tree_adjoined_rcu() usage"); 220885fc8acSChristian Brauner 221885fc8acSChristian Brauner if (previous) 222885fc8acSChristian Brauner list = rcu_dereference(list_bidir_prev_rcu(&ns->ns_list_node)); 223885fc8acSChristian Brauner else 224885fc8acSChristian Brauner list = rcu_dereference(list_next_rcu(&ns->ns_list_node)); 225885fc8acSChristian Brauner if (list_is_head(list, &ns_tree->ns_list)) 226885fc8acSChristian Brauner return ERR_PTR(-ENOENT); 227885fc8acSChristian Brauner 228885fc8acSChristian Brauner VFS_WARN_ON_ONCE(list_entry_rcu(list, struct ns_common, ns_list_node)->ops->type != ns_tree->type); 229885fc8acSChristian Brauner 230885fc8acSChristian Brauner return list_entry_rcu(list, struct ns_common, ns_list_node); 231885fc8acSChristian Brauner } 232885fc8acSChristian Brauner 233885fc8acSChristian Brauner /** 234885fc8acSChristian Brauner * ns_tree_gen_id - generate a new namespace id 235885fc8acSChristian Brauner * @ns: namespace to generate id for 236885fc8acSChristian Brauner * 237885fc8acSChristian Brauner * Generates a new namespace id and assigns it to the namespace. All 238885fc8acSChristian Brauner * namespaces types share the same id space and thus can be compared 239885fc8acSChristian Brauner * directly. IOW, when two ids of two namespace are equal, they are 240885fc8acSChristian Brauner * identical. 241885fc8acSChristian Brauner */ 242885fc8acSChristian Brauner u64 ns_tree_gen_id(struct ns_common *ns) 243885fc8acSChristian Brauner { 244885fc8acSChristian Brauner guard(preempt)(); 245885fc8acSChristian Brauner ns->ns_id = gen_cookie_next(&namespace_cookie); 246885fc8acSChristian Brauner return ns->ns_id; 247885fc8acSChristian Brauner } 248