mirror of https://github.com/torvalds/linux.git
nstree: introduce a unified tree
This will allow userspace to lookup and stat a namespace simply by its identifier without having to know what type of namespace it is. Link: https://patch.msgid.link/20251029-work-namespace-nstree-listns-v4-13-2e6f823ebdc0@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
parent
8895d2a3db
commit
2ccaebc686
|
|
@ -109,6 +109,9 @@ struct ns_common {
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
u64 ns_id;
|
u64 ns_id;
|
||||||
|
struct /* global namespace rbtree and list */ {
|
||||||
|
struct rb_node ns_unified_tree_node;
|
||||||
|
};
|
||||||
struct /* per type rbtree and list */ {
|
struct /* per type rbtree and list */ {
|
||||||
struct rb_node ns_tree_node;
|
struct rb_node ns_tree_node;
|
||||||
struct list_head ns_list_node;
|
struct list_head ns_list_node;
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
|
||||||
ns->ns_id = 0;
|
ns->ns_id = 0;
|
||||||
ns->ns_type = ns_type;
|
ns->ns_type = ns_type;
|
||||||
RB_CLEAR_NODE(&ns->ns_tree_node);
|
RB_CLEAR_NODE(&ns->ns_tree_node);
|
||||||
|
RB_CLEAR_NODE(&ns->ns_unified_tree_node);
|
||||||
INIT_LIST_HEAD(&ns->ns_list_node);
|
INIT_LIST_HEAD(&ns->ns_list_node);
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_VFS
|
#ifdef CONFIG_DEBUG_VFS
|
||||||
|
|
|
||||||
|
|
@ -5,31 +5,30 @@
|
||||||
#include <linux/proc_ns.h>
|
#include <linux/proc_ns.h>
|
||||||
#include <linux/vfsdebug.h>
|
#include <linux/vfsdebug.h>
|
||||||
|
|
||||||
|
static __cacheline_aligned_in_smp DEFINE_SEQLOCK(ns_tree_lock);
|
||||||
|
static struct rb_root ns_unified_tree = RB_ROOT; /* protected by ns_tree_lock */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ns_tree - Namespace tree
|
* struct ns_tree - Namespace tree
|
||||||
* @ns_tree: Rbtree of namespaces of a particular type
|
* @ns_tree: Rbtree of namespaces of a particular type
|
||||||
* @ns_list: Sequentially walkable list of all namespaces of this type
|
* @ns_list: Sequentially walkable list of all namespaces of this type
|
||||||
* @ns_tree_lock: Seqlock to protect the tree and list
|
|
||||||
* @type: type of namespaces in this tree
|
* @type: type of namespaces in this tree
|
||||||
*/
|
*/
|
||||||
struct ns_tree {
|
struct ns_tree {
|
||||||
struct rb_root ns_tree;
|
struct rb_root ns_tree;
|
||||||
struct list_head ns_list;
|
struct list_head ns_list;
|
||||||
seqlock_t ns_tree_lock;
|
int type;
|
||||||
int type;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ns_tree mnt_ns_tree = {
|
struct ns_tree mnt_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(mnt_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(mnt_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(mnt_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWNS,
|
.type = CLONE_NEWNS,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ns_tree net_ns_tree = {
|
struct ns_tree net_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(net_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(net_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(net_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWNET,
|
.type = CLONE_NEWNET,
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(net_ns_tree);
|
EXPORT_SYMBOL_GPL(net_ns_tree);
|
||||||
|
|
@ -37,42 +36,36 @@ EXPORT_SYMBOL_GPL(net_ns_tree);
|
||||||
struct ns_tree uts_ns_tree = {
|
struct ns_tree uts_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(uts_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(uts_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(uts_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWUTS,
|
.type = CLONE_NEWUTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ns_tree user_ns_tree = {
|
struct ns_tree user_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(user_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(user_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(user_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWUSER,
|
.type = CLONE_NEWUSER,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ns_tree ipc_ns_tree = {
|
struct ns_tree ipc_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(ipc_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(ipc_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(ipc_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWIPC,
|
.type = CLONE_NEWIPC,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ns_tree pid_ns_tree = {
|
struct ns_tree pid_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(pid_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(pid_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(pid_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWPID,
|
.type = CLONE_NEWPID,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ns_tree cgroup_ns_tree = {
|
struct ns_tree cgroup_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(cgroup_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(cgroup_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(cgroup_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWCGROUP,
|
.type = CLONE_NEWCGROUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ns_tree time_ns_tree = {
|
struct ns_tree time_ns_tree = {
|
||||||
.ns_tree = RB_ROOT,
|
.ns_tree = RB_ROOT,
|
||||||
.ns_list = LIST_HEAD_INIT(time_ns_tree.ns_list),
|
.ns_list = LIST_HEAD_INIT(time_ns_tree.ns_list),
|
||||||
.ns_tree_lock = __SEQLOCK_UNLOCKED(time_ns_tree.ns_tree_lock),
|
|
||||||
.type = CLONE_NEWTIME,
|
.type = CLONE_NEWTIME,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -85,6 +78,13 @@ static inline struct ns_common *node_to_ns(const struct rb_node *node)
|
||||||
return rb_entry(node, struct ns_common, ns_tree_node);
|
return rb_entry(node, struct ns_common, ns_tree_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct ns_common *node_to_ns_unified(const struct rb_node *node)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
return rb_entry(node, struct ns_common, ns_unified_tree_node);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int ns_cmp(struct rb_node *a, const struct rb_node *b)
|
static inline int ns_cmp(struct rb_node *a, const struct rb_node *b)
|
||||||
{
|
{
|
||||||
struct ns_common *ns_a = node_to_ns(a);
|
struct ns_common *ns_a = node_to_ns(a);
|
||||||
|
|
@ -99,15 +99,27 @@ static inline int ns_cmp(struct rb_node *a, const struct rb_node *b)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int ns_cmp_unified(struct rb_node *a, const struct rb_node *b)
|
||||||
|
{
|
||||||
|
struct ns_common *ns_a = node_to_ns_unified(a);
|
||||||
|
struct ns_common *ns_b = node_to_ns_unified(b);
|
||||||
|
u64 ns_id_a = ns_a->ns_id;
|
||||||
|
u64 ns_id_b = ns_b->ns_id;
|
||||||
|
|
||||||
|
if (ns_id_a < ns_id_b)
|
||||||
|
return -1;
|
||||||
|
if (ns_id_a > ns_id_b)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree)
|
void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree)
|
||||||
{
|
{
|
||||||
struct rb_node *node, *prev;
|
struct rb_node *node, *prev;
|
||||||
|
|
||||||
VFS_WARN_ON_ONCE(!ns->ns_id);
|
VFS_WARN_ON_ONCE(!ns->ns_id);
|
||||||
|
|
||||||
write_seqlock(&ns_tree->ns_tree_lock);
|
write_seqlock(&ns_tree_lock);
|
||||||
|
|
||||||
VFS_WARN_ON_ONCE(ns->ns_type != ns_tree->type);
|
|
||||||
|
|
||||||
node = rb_find_add_rcu(&ns->ns_tree_node, &ns_tree->ns_tree, ns_cmp);
|
node = rb_find_add_rcu(&ns->ns_tree_node, &ns_tree->ns_tree, ns_cmp);
|
||||||
/*
|
/*
|
||||||
|
|
@ -120,7 +132,8 @@ void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree)
|
||||||
else
|
else
|
||||||
list_add_rcu(&ns->ns_list_node, &node_to_ns(prev)->ns_list_node);
|
list_add_rcu(&ns->ns_list_node, &node_to_ns(prev)->ns_list_node);
|
||||||
|
|
||||||
write_sequnlock(&ns_tree->ns_tree_lock);
|
rb_find_add_rcu(&ns->ns_unified_tree_node, &ns_unified_tree, ns_cmp_unified);
|
||||||
|
write_sequnlock(&ns_tree_lock);
|
||||||
|
|
||||||
VFS_WARN_ON_ONCE(node);
|
VFS_WARN_ON_ONCE(node);
|
||||||
|
|
||||||
|
|
@ -139,11 +152,12 @@ void __ns_tree_remove(struct ns_common *ns, struct ns_tree *ns_tree)
|
||||||
VFS_WARN_ON_ONCE(list_empty(&ns->ns_list_node));
|
VFS_WARN_ON_ONCE(list_empty(&ns->ns_list_node));
|
||||||
VFS_WARN_ON_ONCE(ns->ns_type != ns_tree->type);
|
VFS_WARN_ON_ONCE(ns->ns_type != ns_tree->type);
|
||||||
|
|
||||||
write_seqlock(&ns_tree->ns_tree_lock);
|
write_seqlock(&ns_tree_lock);
|
||||||
rb_erase(&ns->ns_tree_node, &ns_tree->ns_tree);
|
rb_erase(&ns->ns_tree_node, &ns_tree->ns_tree);
|
||||||
|
rb_erase(&ns->ns_unified_tree_node, &ns_unified_tree);
|
||||||
list_bidir_del_rcu(&ns->ns_list_node);
|
list_bidir_del_rcu(&ns->ns_list_node);
|
||||||
RB_CLEAR_NODE(&ns->ns_tree_node);
|
RB_CLEAR_NODE(&ns->ns_tree_node);
|
||||||
write_sequnlock(&ns_tree->ns_tree_lock);
|
write_sequnlock(&ns_tree_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__ns_tree_remove);
|
EXPORT_SYMBOL_GPL(__ns_tree_remove);
|
||||||
|
|
||||||
|
|
@ -159,6 +173,17 @@ static int ns_find(const void *key, const struct rb_node *node)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ns_find_unified(const void *key, const struct rb_node *node)
|
||||||
|
{
|
||||||
|
const u64 ns_id = *(u64 *)key;
|
||||||
|
const struct ns_common *ns = node_to_ns_unified(node);
|
||||||
|
|
||||||
|
if (ns_id < ns->ns_id)
|
||||||
|
return -1;
|
||||||
|
if (ns_id > ns->ns_id)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct ns_tree *ns_tree_from_type(int ns_type)
|
static struct ns_tree *ns_tree_from_type(int ns_type)
|
||||||
{
|
{
|
||||||
|
|
@ -184,28 +209,51 @@ static struct ns_tree *ns_tree_from_type(int ns_type)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ns_common *ns_tree_lookup_rcu(u64 ns_id, int ns_type)
|
static struct ns_common *__ns_unified_tree_lookup_rcu(u64 ns_id)
|
||||||
|
{
|
||||||
|
struct rb_node *node;
|
||||||
|
unsigned int seq;
|
||||||
|
|
||||||
|
do {
|
||||||
|
seq = read_seqbegin(&ns_tree_lock);
|
||||||
|
node = rb_find_rcu(&ns_id, &ns_unified_tree, ns_find_unified);
|
||||||
|
if (node)
|
||||||
|
break;
|
||||||
|
} while (read_seqretry(&ns_tree_lock, seq));
|
||||||
|
|
||||||
|
return node_to_ns_unified(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ns_common *__ns_tree_lookup_rcu(u64 ns_id, int ns_type)
|
||||||
{
|
{
|
||||||
struct ns_tree *ns_tree;
|
struct ns_tree *ns_tree;
|
||||||
struct rb_node *node;
|
struct rb_node *node;
|
||||||
unsigned int seq;
|
unsigned int seq;
|
||||||
|
|
||||||
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "suspicious ns_tree_lookup_rcu() usage");
|
|
||||||
|
|
||||||
ns_tree = ns_tree_from_type(ns_type);
|
ns_tree = ns_tree_from_type(ns_type);
|
||||||
if (!ns_tree)
|
if (!ns_tree)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
seq = read_seqbegin(&ns_tree->ns_tree_lock);
|
seq = read_seqbegin(&ns_tree_lock);
|
||||||
node = rb_find_rcu(&ns_id, &ns_tree->ns_tree, ns_find);
|
node = rb_find_rcu(&ns_id, &ns_tree->ns_tree, ns_find);
|
||||||
if (node)
|
if (node)
|
||||||
break;
|
break;
|
||||||
} while (read_seqretry(&ns_tree->ns_tree_lock, seq));
|
} while (read_seqretry(&ns_tree_lock, seq));
|
||||||
|
|
||||||
return node_to_ns(node);
|
return node_to_ns(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ns_common *ns_tree_lookup_rcu(u64 ns_id, int ns_type)
|
||||||
|
{
|
||||||
|
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "suspicious ns_tree_lookup_rcu() usage");
|
||||||
|
|
||||||
|
if (ns_type)
|
||||||
|
return __ns_tree_lookup_rcu(ns_id, ns_type);
|
||||||
|
|
||||||
|
return __ns_unified_tree_lookup_rcu(ns_id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ns_tree_adjoined_rcu - find the next/previous namespace in the same
|
* ns_tree_adjoined_rcu - find the next/previous namespace in the same
|
||||||
* tree
|
* tree
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue