xref: /linux/kernel/nstree.c (revision 18b19abc3709b109676ffd1f48dcd332c2e477d4)
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 
710cdfcd3SChristian Brauner /**
810cdfcd3SChristian Brauner  * struct ns_tree - Namespace tree
910cdfcd3SChristian Brauner  * @ns_tree: Rbtree of namespaces of a particular type
1010cdfcd3SChristian Brauner  * @ns_list: Sequentially walkable list of all namespaces of this type
1110cdfcd3SChristian Brauner  * @ns_tree_lock: Seqlock to protect the tree and list
1210cdfcd3SChristian Brauner  * @type: type of namespaces in this tree
1310cdfcd3SChristian Brauner  */
1410cdfcd3SChristian Brauner struct ns_tree {
1510cdfcd3SChristian Brauner        struct rb_root ns_tree;
1610cdfcd3SChristian Brauner        struct list_head ns_list;
1710cdfcd3SChristian Brauner        seqlock_t ns_tree_lock;
1810cdfcd3SChristian Brauner        int type;
1910cdfcd3SChristian Brauner };
2010cdfcd3SChristian 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 
node_to_ns(const struct rb_node * node)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 
ns_cmp(struct rb_node * a,const struct rb_node * b)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 
__ns_tree_add_raw(struct ns_common * ns,struct ns_tree * ns_tree)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 
109*4055526dSChristian Brauner 	VFS_WARN_ON_ONCE(ns->ns_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 
__ns_tree_remove(struct ns_common * ns,struct ns_tree * ns_tree)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));
131*4055526dSChristian Brauner 	VFS_WARN_ON_ONCE(ns->ns_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 
ns_find(const void * key,const struct rb_node * node)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 
ns_tree_from_type(int ns_type)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 
ns_tree_lookup_rcu(u64 ns_id,int ns_type)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 
200*4055526dSChristian Brauner 	VFS_WARN_ON_ONCE(node_to_ns(node)->ns_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  */
__ns_tree_adjoined_rcu(struct ns_common * ns,struct ns_tree * ns_tree,bool previous)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 
228*4055526dSChristian Brauner 	VFS_WARN_ON_ONCE(list_entry_rcu(list, struct ns_common, ns_list_node)->ns_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  */
ns_tree_gen_id(struct ns_common * ns)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