Merge patch series "ns: rework common initialization"

Christian Brauner <brauner@kernel.org> says:

The current scheme still involves a lot of open-coding and copy-pasing
and bleeds a lot of unnecessary details into actual namespace
implementers. Encapsulate it in the common helpers and simplify it all.

* patches from https://lore.kernel.org/20250917-work-namespace-ns_common-v1-0-1b3bda8ef8f2@kernel.org:
  ns: add ns_common_free()
  nscommon: simplify initialization
  net: centralize ns_common initialization
  mnt: simplify ns_common_init() handling
  nsfs: add inode number for anon namespace
  cgroup: split namespace into separate header
  nscommon: move to separate file
  mnt: expose pointer to init_mnt_ns
  uts: split namespace into separate header

Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner
2025-09-19 10:14:45 +02:00
18 changed files with 235 additions and 178 deletions

View File

@@ -4082,7 +4082,7 @@ static void dec_mnt_namespaces(struct ucounts *ucounts)
static void free_mnt_ns(struct mnt_namespace *ns)
{
if (!is_anon_ns(ns))
ns_free_inum(&ns->ns);
ns_common_free(ns);
dec_mnt_namespaces(ns->ucounts);
mnt_ns_tree_remove(ns);
}
@@ -4103,7 +4103,10 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool a
return ERR_PTR(-ENOMEM);
}
ret = ns_common_init(&new_ns->ns, &mntns_operations, !anon);
if (anon)
ret = ns_common_init_inum(new_ns, &mntns_operations, MNT_NS_ANON_INO);
else
ret = ns_common_init(new_ns, &mntns_operations);
if (ret) {
kfree(new_ns);
dec_mnt_namespaces(ucounts);
@@ -4151,7 +4154,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
new = copy_tree(old, old->mnt.mnt_root, copy_flags);
if (IS_ERR(new)) {
namespace_unlock();
ns_free_inum(&new_ns->ns);
ns_common_free(ns);
dec_mnt_namespaces(new_ns->ucounts);
mnt_ns_release(new_ns);
return ERR_CAST(new);
@@ -6008,27 +6011,32 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
return ret;
}
struct mnt_namespace init_mnt_ns = {
.ns.inum = PROC_MNT_INIT_INO,
.ns.ops = &mntns_operations,
.user_ns = &init_user_ns,
.ns.count = REFCOUNT_INIT(1),
.passive = REFCOUNT_INIT(1),
.mounts = RB_ROOT,
.poll = __WAIT_QUEUE_HEAD_INITIALIZER(init_mnt_ns.poll),
};
static void __init init_mount_tree(void)
{
struct vfsmount *mnt;
struct mount *m;
struct mnt_namespace *ns;
struct path root;
mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", NULL);
if (IS_ERR(mnt))
panic("Can't create rootfs");
ns = alloc_mnt_ns(&init_user_ns, true);
if (IS_ERR(ns))
panic("Can't allocate initial namespace");
ns->ns.inum = PROC_MNT_INIT_INO;
m = real_mount(mnt);
ns->root = m;
ns->nr_mounts = 1;
mnt_add_to_ns(ns, m);
init_task.nsproxy->mnt_ns = ns;
get_mnt_ns(ns);
init_mnt_ns.root = m;
init_mnt_ns.nr_mounts = 1;
mnt_add_to_ns(&init_mnt_ns, m);
init_task.nsproxy->mnt_ns = &init_mnt_ns;
get_mnt_ns(&init_mnt_ns);
root.mnt = mnt;
root.dentry = mnt->mnt_root;
@@ -6036,7 +6044,7 @@ static void __init init_mount_tree(void)
set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);
ns_tree_add(ns);
ns_tree_add(&init_mnt_ns);
}
void __init mnt_init(void)

View File

@@ -27,6 +27,7 @@
#include <linux/kernel_stat.h>
#include <linux/cgroup-defs.h>
#include <linux/cgroup_namespace.h>
struct kernel_clone_args;
@@ -783,56 +784,6 @@ static inline void cgroup_sk_free(struct sock_cgroup_data *skcd) {}
#endif /* CONFIG_CGROUP_DATA */
struct cgroup_namespace {
struct ns_common ns;
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct css_set *root_cset;
};
extern struct cgroup_namespace init_cgroup_ns;
#ifdef CONFIG_CGROUPS
static inline struct cgroup_namespace *to_cg_ns(struct ns_common *ns)
{
return container_of(ns, struct cgroup_namespace, ns);
}
void free_cgroup_ns(struct cgroup_namespace *ns);
struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
struct user_namespace *user_ns,
struct cgroup_namespace *old_ns);
int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
struct cgroup_namespace *ns);
static inline void get_cgroup_ns(struct cgroup_namespace *ns)
{
refcount_inc(&ns->ns.count);
}
static inline void put_cgroup_ns(struct cgroup_namespace *ns)
{
if (refcount_dec_and_test(&ns->ns.count))
free_cgroup_ns(ns);
}
#else /* !CONFIG_CGROUPS */
static inline void free_cgroup_ns(struct cgroup_namespace *ns) { }
static inline struct cgroup_namespace *
copy_cgroup_ns(unsigned long flags, struct user_namespace *user_ns,
struct cgroup_namespace *old_ns)
{
return old_ns;
}
static inline void get_cgroup_ns(struct cgroup_namespace *ns) { }
static inline void put_cgroup_ns(struct cgroup_namespace *ns) { }
#endif /* !CONFIG_CGROUPS */
#ifdef CONFIG_CGROUPS

View File

@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_CGROUP_NAMESPACE_H
#define _LINUX_CGROUP_NAMESPACE_H
struct cgroup_namespace {
struct ns_common ns;
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct css_set *root_cset;
};
extern struct cgroup_namespace init_cgroup_ns;
#ifdef CONFIG_CGROUPS
static inline struct cgroup_namespace *to_cg_ns(struct ns_common *ns)
{
return container_of(ns, struct cgroup_namespace, ns);
}
void free_cgroup_ns(struct cgroup_namespace *ns);
struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
struct user_namespace *user_ns,
struct cgroup_namespace *old_ns);
int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
struct cgroup_namespace *ns);
static inline void get_cgroup_ns(struct cgroup_namespace *ns)
{
refcount_inc(&ns->ns.count);
}
static inline void put_cgroup_ns(struct cgroup_namespace *ns)
{
if (refcount_dec_and_test(&ns->ns.count))
free_cgroup_ns(ns);
}
#else /* !CONFIG_CGROUPS */
static inline void free_cgroup_ns(struct cgroup_namespace *ns) { }
static inline struct cgroup_namespace *
copy_cgroup_ns(unsigned long flags, struct user_namespace *user_ns,
struct cgroup_namespace *old_ns)
{
return old_ns;
}
static inline void get_cgroup_ns(struct cgroup_namespace *ns) { }
static inline void put_cgroup_ns(struct cgroup_namespace *ns) { }
#endif /* !CONFIG_CGROUPS */
#endif /* _LINUX_CGROUP_NAMESPACE_H */

View File

@@ -11,6 +11,8 @@ struct fs_struct;
struct user_namespace;
struct ns_common;
extern struct mnt_namespace init_mnt_ns;
extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
struct user_namespace *, struct fs_struct *);
extern void put_mnt_ns(struct mnt_namespace *ns);

View File

@@ -16,6 +16,15 @@ struct time_namespace;
struct user_namespace;
struct uts_namespace;
extern struct cgroup_namespace init_cgroup_ns;
extern struct ipc_namespace init_ipc_ns;
extern struct mnt_namespace init_mnt_ns;
extern struct net init_net;
extern struct pid_namespace init_pid_ns;
extern struct time_namespace init_time_ns;
extern struct user_namespace init_user_ns;
extern struct uts_namespace init_uts_ns;
struct ns_common {
struct dentry *stashed;
const struct proc_ns_operations *ops;
@@ -31,6 +40,9 @@ struct ns_common {
};
};
int __ns_common_init(struct ns_common *ns, const struct proc_ns_operations *ops, int inum);
void __ns_common_free(struct ns_common *ns);
#define to_ns_common(__ns) \
_Generic((__ns), \
struct cgroup_namespace *: &(__ns)->ns, \
@@ -42,4 +54,33 @@ struct ns_common {
struct user_namespace *: &(__ns)->ns, \
struct uts_namespace *: &(__ns)->ns)
#define ns_init_inum(__ns) \
_Generic((__ns), \
struct cgroup_namespace *: CGROUP_NS_INIT_INO, \
struct ipc_namespace *: IPC_NS_INIT_INO, \
struct mnt_namespace *: MNT_NS_INIT_INO, \
struct net *: NET_NS_INIT_INO, \
struct pid_namespace *: PID_NS_INIT_INO, \
struct time_namespace *: TIME_NS_INIT_INO, \
struct user_namespace *: USER_NS_INIT_INO, \
struct uts_namespace *: UTS_NS_INIT_INO)
#define ns_init_ns(__ns) \
_Generic((__ns), \
struct cgroup_namespace *: &init_cgroup_ns, \
struct ipc_namespace *: &init_ipc_ns, \
struct mnt_namespace *: &init_mnt_ns, \
struct net *: &init_net, \
struct pid_namespace *: &init_pid_ns, \
struct time_namespace *: &init_time_ns, \
struct user_namespace *: &init_user_ns, \
struct uts_namespace *: &init_uts_ns)
#define ns_common_init(__ns, __ops) \
__ns_common_init(to_ns_common(__ns), __ops, (((__ns) == ns_init_ns(__ns)) ? ns_init_inum(__ns) : 0))
#define ns_common_init_inum(__ns, __ops, __inum) __ns_common_init(to_ns_common(__ns), __ops, __inum)
#define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns)))
#endif

View File

@@ -66,27 +66,6 @@ static inline void proc_free_inum(unsigned int inum) {}
#endif /* CONFIG_PROC_FS */
static inline int ns_common_init(struct ns_common *ns,
const struct proc_ns_operations *ops,
bool alloc_inum)
{
if (alloc_inum) {
int ret;
ret = proc_alloc_inum(&ns->inum);
if (ret)
return ret;
}
refcount_set(&ns->count, 1);
ns->stashed = NULL;
ns->ops = ops;
ns->ns_id = 0;
RB_CLEAR_NODE(&ns->ns_tree_node);
INIT_LIST_HEAD(&ns->ns_list_node);
return 0;
}
#define ns_free_inum(ns) proc_free_inum((ns)->inum)
#define get_proc_ns(inode) ((struct ns_common *)(inode)->i_private)
#endif /* _LINUX_PROC_NS_H */

View File

@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_UTS_NAMESPACE_H
#define _LINUX_UTS_NAMESPACE_H
#include <linux/ns_common.h>
#include <uapi/linux/utsname.h>
struct user_namespace;
extern struct user_namespace init_user_ns;
struct uts_namespace {
struct new_utsname name;
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct ns_common ns;
} __randomize_layout;
extern struct uts_namespace init_uts_ns;
#ifdef CONFIG_UTS_NS
static inline struct uts_namespace *to_uts_ns(struct ns_common *ns)
{
return container_of(ns, struct uts_namespace, ns);
}
static inline void get_uts_ns(struct uts_namespace *ns)
{
refcount_inc(&ns->ns.count);
}
extern struct uts_namespace *copy_utsname(unsigned long flags,
struct user_namespace *user_ns, struct uts_namespace *old_ns);
extern void free_uts_ns(struct uts_namespace *ns);
static inline void put_uts_ns(struct uts_namespace *ns)
{
if (refcount_dec_and_test(&ns->ns.count))
free_uts_ns(ns);
}
void uts_ns_init(void);
#else
static inline void get_uts_ns(struct uts_namespace *ns)
{
}
static inline void put_uts_ns(struct uts_namespace *ns)
{
}
static inline struct uts_namespace *copy_utsname(unsigned long flags,
struct user_namespace *user_ns, struct uts_namespace *old_ns)
{
if (flags & CLONE_NEWUTS)
return ERR_PTR(-EINVAL);
return old_ns;
}
static inline void uts_ns_init(void)
{
}
#endif
#endif /* _LINUX_UTS_NAMESPACE_H */

View File

@@ -7,7 +7,7 @@
#include <linux/nsproxy.h>
#include <linux/ns_common.h>
#include <linux/err.h>
#include <uapi/linux/utsname.h>
#include <linux/uts_namespace.h>
enum uts_proc {
UTS_PROC_ARCH,
@@ -18,62 +18,6 @@ enum uts_proc {
UTS_PROC_DOMAINNAME,
};
struct user_namespace;
extern struct user_namespace init_user_ns;
struct uts_namespace {
struct new_utsname name;
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct ns_common ns;
} __randomize_layout;
extern struct uts_namespace init_uts_ns;
#ifdef CONFIG_UTS_NS
static inline struct uts_namespace *to_uts_ns(struct ns_common *ns)
{
return container_of(ns, struct uts_namespace, ns);
}
static inline void get_uts_ns(struct uts_namespace *ns)
{
refcount_inc(&ns->ns.count);
}
extern struct uts_namespace *copy_utsname(unsigned long flags,
struct user_namespace *user_ns, struct uts_namespace *old_ns);
extern void free_uts_ns(struct uts_namespace *ns);
static inline void put_uts_ns(struct uts_namespace *ns)
{
if (refcount_dec_and_test(&ns->ns.count))
free_uts_ns(ns);
}
void uts_ns_init(void);
#else
static inline void get_uts_ns(struct uts_namespace *ns)
{
}
static inline void put_uts_ns(struct uts_namespace *ns)
{
}
static inline struct uts_namespace *copy_utsname(unsigned long flags,
struct user_namespace *user_ns, struct uts_namespace *old_ns)
{
if (flags & CLONE_NEWUTS)
return ERR_PTR(-EINVAL);
return old_ns;
}
static inline void uts_ns_init(void)
{
}
#endif
#ifdef CONFIG_PROC_SYSCTL
extern void uts_proc_notify(enum uts_proc proc);
#else

View File

@@ -53,6 +53,9 @@ enum init_ns_ino {
TIME_NS_INIT_INO = 0xEFFFFFFAU,
NET_NS_INIT_INO = 0xEFFFFFF9U,
MNT_NS_INIT_INO = 0xEFFFFFF8U,
#ifdef __KERNEL__
MNT_NS_ANON_INO = 0xEFFFFFF7U,
#endif
};
struct nsfs_file_handle {

View File

@@ -62,7 +62,7 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
if (ns == NULL)
goto fail_dec;
err = ns_common_init(&ns->ns, &ipcns_operations, true);
err = ns_common_init(ns, &ipcns_operations);
if (err)
goto fail_free;
@@ -97,7 +97,7 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
fail_put:
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
ns_common_free(ns);
fail_free:
kfree(ns);
fail_dec:
@@ -161,7 +161,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)
dec_ipc_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
ns_common_free(ns);
kfree(ns);
}

View File

@@ -8,7 +8,7 @@ obj-y = fork.o exec_domain.o panic.o \
sysctl.o capability.o ptrace.o user.o \
signal.o sys.o umh.o workqueue.o pid.o task_work.o \
extable.o params.o \
kthread.o sys_ni.o nsproxy.o nstree.o \
kthread.o sys_ni.o nsproxy.o nstree.o nscommon.o \
notifier.o ksysfs.o cred.o reboot.o \
async.o range.o smpboot.o ucount.o regset.o ksyms_common.o

View File

@@ -27,7 +27,7 @@ static struct cgroup_namespace *alloc_cgroup_ns(void)
new_ns = kzalloc(sizeof(struct cgroup_namespace), GFP_KERNEL_ACCOUNT);
if (!new_ns)
return ERR_PTR(-ENOMEM);
ret = ns_common_init(&new_ns->ns, &cgroupns_operations, true);
ret = ns_common_init(new_ns, &cgroupns_operations);
if (ret)
return ERR_PTR(ret);
ns_tree_add(new_ns);
@@ -40,7 +40,7 @@ void free_cgroup_ns(struct cgroup_namespace *ns)
put_css_set(ns->root_cset);
dec_cgroup_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
ns_common_free(ns);
/* Concurrent nstree traversal depends on a grace period. */
kfree_rcu(ns, ns.ns_rcu);
}

25
kernel/nscommon.c Normal file
View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/ns_common.h>
#include <linux/proc_ns.h>
int __ns_common_init(struct ns_common *ns, const struct proc_ns_operations *ops, int inum)
{
refcount_set(&ns->count, 1);
ns->stashed = NULL;
ns->ops = ops;
ns->ns_id = 0;
RB_CLEAR_NODE(&ns->ns_tree_node);
INIT_LIST_HEAD(&ns->ns_list_node);
if (inum) {
ns->inum = inum;
return 0;
}
return proc_alloc_inum(&ns->inum);
}
void __ns_common_free(struct ns_common *ns)
{
proc_free_inum(ns->inum);
}

View File

@@ -103,7 +103,7 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns
if (ns->pid_cachep == NULL)
goto out_free_idr;
err = ns_common_init(&ns->ns, &pidns_operations, true);
err = ns_common_init(ns, &pidns_operations);
if (err)
goto out_free_idr;
@@ -127,7 +127,7 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns
return ns;
out_free_inum:
ns_free_inum(&ns->ns);
ns_common_free(ns);
out_free_idr:
idr_destroy(&ns->idr);
kmem_cache_free(pid_ns_cachep, ns);
@@ -152,7 +152,7 @@ static void destroy_pid_namespace(struct pid_namespace *ns)
ns_tree_remove(ns);
unregister_pidns_sysctls(ns);
ns_free_inum(&ns->ns);
ns_common_free(ns);
idr_destroy(&ns->idr);
call_rcu(&ns->rcu, delayed_free_pidns);

View File

@@ -97,7 +97,7 @@ static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
if (!ns->vvar_page)
goto fail_free;
err = ns_common_init(&ns->ns, &timens_operations, true);
err = ns_common_init(ns, &timens_operations);
if (err)
goto fail_free_page;
@@ -255,7 +255,7 @@ void free_time_ns(struct time_namespace *ns)
ns_tree_remove(ns);
dec_time_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
ns_common_free(ns);
__free_page(ns->vvar_page);
/* Concurrent nstree traversal depends on a grace period. */
kfree_rcu(ns, ns.ns_rcu);

View File

@@ -126,7 +126,7 @@ int create_user_ns(struct cred *new)
ns->parent_could_setfcap = cap_raised(new->cap_effective, CAP_SETFCAP);
ret = ns_common_init(&ns->ns, &userns_operations, true);
ret = ns_common_init(ns, &userns_operations);
if (ret)
goto fail_free;
@@ -165,7 +165,7 @@ int create_user_ns(struct cred *new)
#ifdef CONFIG_PERSISTENT_KEYRINGS
key_put(ns->persistent_keyring_register);
#endif
ns_free_inum(&ns->ns);
ns_common_free(ns);
fail_free:
kmem_cache_free(user_ns_cachep, ns);
fail_dec:
@@ -220,7 +220,7 @@ static void free_user_ns(struct work_struct *work)
#endif
retire_userns_sysctls(ns);
key_free_user_ns(ns);
ns_free_inum(&ns->ns);
ns_common_free(ns);
/* Concurrent nstree traversal depends on a grace period. */
kfree_rcu(ns, ns.ns_rcu);
dec_user_namespaces(ucounts);

View File

@@ -50,7 +50,7 @@ static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
if (!ns)
goto fail_dec;
err = ns_common_init(&ns->ns, &utsns_operations, true);
err = ns_common_init(ns, &utsns_operations);
if (err)
goto fail_free;
@@ -98,7 +98,7 @@ void free_uts_ns(struct uts_namespace *ns)
ns_tree_remove(ns);
dec_uts_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
ns_common_free(ns);
/* Concurrent nstree traversal depends on a grace period. */
kfree_rcu(ns, ns.ns_rcu);
}

View File

@@ -409,7 +409,7 @@ static __net_init int preinit_net(struct net *net, struct user_namespace *user_n
ns_ops = NULL;
#endif
ret = ns_common_init(&net->ns, ns_ops, false);
ret = ns_common_init(net, ns_ops);
if (ret)
return ret;
@@ -590,6 +590,7 @@ struct net *copy_net_ns(unsigned long flags,
if (rv < 0) {
put_userns:
ns_common_free(net);
#ifdef CONFIG_KEYS
key_remove_domain(net->key_domain);
#endif
@@ -712,6 +713,7 @@ static void cleanup_net(struct work_struct *work)
/* Finally it is safe to free my network namespace structure */
list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
list_del_init(&net->exit_list);
ns_common_free(net);
dec_net_namespaces(net->ucounts);
#ifdef CONFIG_KEYS
key_remove_domain(net->key_domain);
@@ -831,31 +833,12 @@ static void net_ns_net_debugfs(struct net *net)
static __net_init int net_ns_net_init(struct net *net)
{
int ret = 0;
if (net == &init_net)
net->ns.inum = PROC_NET_INIT_INO;
else
ret = proc_alloc_inum(&to_ns_common(net)->inum);
if (ret)
return ret;
net_ns_net_debugfs(net);
return 0;
}
static __net_exit void net_ns_net_exit(struct net *net)
{
/*
* Initial network namespace doesn't exit so we don't need any
* special checks here.
*/
ns_free_inum(&net->ns);
}
static struct pernet_operations __net_initdata net_ns_ops = {
.init = net_ns_net_init,
.exit = net_ns_net_exit,
};
static const struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = {