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