xref: /linux/kernel/nstree.c (revision 885fc8ac0a4dc70f5d87b80b0977292870e35c60)
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