Re: [PATCH 3/8] cgroup: introduce cgroup namespaces

From: Serge E. Hallyn
Date: Mon Jan 04 2016 - 15:21:06 EST


On Mon, Jan 04, 2016 at 01:54:48PM -0600, serge.hallyn@xxxxxxxxxx wrote:
> From: Aditya Kali <adityakali@xxxxxxxxxx>
>
> Introduce the ability to create new cgroup namespace. The newly created
> cgroup namespace remembers the cgroup of the process at the point
> of creation of the cgroup namespace (referred as cgroupns-root).
> The main purpose of cgroup namespace is to virtualize the contents
> of /proc/self/cgroup file. Processes inside a cgroup namespace
> are only able to see paths relative to their namespace root
> (unless they are moved outside of their cgroupns-root, at which point
> they will see a relative path from their cgroupns-root).
> For a correctly setup container this enables container-tools
> (like libcontainer, lxc, lmctfy, etc.) to create completely virtualized
> containers without leaking system level cgroup hierarchy to the task.
> This patch only implements the 'unshare' part of the cgroupns.
>
> Signed-off-by: Aditya Kali <adityakali@xxxxxxxxxx>
> Signed-off-by: Serge Hallyn <serge.hallyn@xxxxxxxxxxxxx>
> ---
> Changelog: 2015-11-24
> - move cgroup_namespace.c into cgroup.c (and .h)
> - reformatting
> - make get_cgroup_ns return void
> - rename ns->root_cgrps to root_cset.
> Changelog: 2015-12-08
> - Move init_cgroup_ns to other variable declarations
> - Remove accidental conversion of put-css_set to inline
> - Drop BUG_ON(NULL)
> - Remove unneeded pre declaration of struct cgroupns_operations.
> - cgroup.h: collect common ns declerations
> Changelog: 2015-12-09
> - cgroup.h: move ns declarations to bottom
> - cgroup.c: undo all accidental conversions to inline
> Changelog: 2015-12-22
> - update for new kernfs_path_from_node() return value. Since
> cgroup_path was already gpl-exported, I abstained from updating
> its return value.
> Changelog: 2015-12-23
> - cgroup_path(): use init_cgroup_ns when in interupt context.
> Changelog: 2015-01-02
> - move to_cg_ns definition forward in patch series
> - cgroup_release_agent: grab css_set_lock around cgroup_path()
> - leave cgroup_path non-namespaced, use cgroup_path_ns when
> namespaced path is desired.
> ---
> fs/proc/namespaces.c | 3 +
> include/linux/cgroup.h | 56 +++++++++++++--
> include/linux/nsproxy.h | 2 +
> include/linux/proc_ns.h | 4 ++
> kernel/cgroup.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++-
> kernel/cpuset.c | 3 +-
> kernel/fork.c | 2 +-
> kernel/nsproxy.c | 21 +++++-
> kernel/sched/debug.c | 3 +-
> 9 files changed, 257 insertions(+), 14 deletions(-)
>
> diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
> index f6e8354..bd61075 100644
> --- a/fs/proc/namespaces.c
> +++ b/fs/proc/namespaces.c
> @@ -28,6 +28,9 @@ static const struct proc_ns_operations *ns_entries[] = {
> &userns_operations,
> #endif
> &mntns_operations,
> +#ifdef CONFIG_CGROUPS
> + &cgroupns_operations,
> +#endif
> };
>
> static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie)
> diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
> index 9d70b48..149ae0a 100644
> --- a/include/linux/cgroup.h
> +++ b/include/linux/cgroup.h
> @@ -17,6 +17,11 @@
> #include <linux/seq_file.h>
> #include <linux/kernfs.h>
> #include <linux/jump_label.h>
> +#include <linux/nsproxy.h>
> +#include <linux/types.h>
> +#include <linux/ns_common.h>
> +#include <linux/nsproxy.h>
> +#include <linux/user_namespace.h>
>
> #include <linux/cgroup-defs.h>
>
> @@ -532,12 +537,6 @@ static inline int cgroup_name(struct cgroup *cgrp, char *buf, size_t buflen)
> return kernfs_name(cgrp->kn, buf, buflen);
> }
>
> -static inline char * __must_check cgroup_path(struct cgroup *cgrp, char *buf,
> - size_t buflen)
> -{
> - return kernfs_path(cgrp->kn, buf, buflen);
> -}
> -
> static inline void pr_cont_cgroup_name(struct cgroup *cgrp)
> {
> pr_cont_kernfs_name(cgrp->kn);
> @@ -570,4 +569,49 @@ static inline int cgroup_init(void) { return 0; }
>
> #endif /* !CONFIG_CGROUPS */
>
> +struct cgroup_namespace {
> + atomic_t count;
> + struct ns_common ns;
> + struct user_namespace *user_ns;
> + struct css_set *root_cset;
> +};
> +
> +extern struct cgroup_namespace init_cgroup_ns;
> +
> +#ifdef CONFIG_CGROUPS
> +
> +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);
> +
> +char *cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
> + struct cgroup_namespace *ns);
> +char *cgroup_path(struct cgroup *cgrp, char *buf, size_t buflen);
> +
> +#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;
> +}
> +
> +#endif /* !CONFIG_CGROUPS */
> +
> +static inline void get_cgroup_ns(struct cgroup_namespace *ns)
> +{
> + if (ns)
> + atomic_inc(&ns->count);
> +}
> +
> +static inline void put_cgroup_ns(struct cgroup_namespace *ns)
> +{
> + if (ns && atomic_dec_and_test(&ns->count))
> + free_cgroup_ns(ns);
> +}
> +
> #endif /* _LINUX_CGROUP_H */
> diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
> index 35fa08f..ac0d65b 100644
> --- a/include/linux/nsproxy.h
> +++ b/include/linux/nsproxy.h
> @@ -8,6 +8,7 @@ struct mnt_namespace;
> struct uts_namespace;
> struct ipc_namespace;
> struct pid_namespace;
> +struct cgroup_namespace;
> struct fs_struct;
>
> /*
> @@ -33,6 +34,7 @@ struct nsproxy {
> struct mnt_namespace *mnt_ns;
> struct pid_namespace *pid_ns_for_children;
> struct net *net_ns;
> + struct cgroup_namespace *cgroup_ns;
> };
> extern struct nsproxy init_nsproxy;
>
> diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
> index 42dfc61..de0e771 100644
> --- a/include/linux/proc_ns.h
> +++ b/include/linux/proc_ns.h
> @@ -9,6 +9,8 @@
> struct pid_namespace;
> struct nsproxy;
> struct path;
> +struct task_struct;
> +struct inode;
>
> struct proc_ns_operations {
> const char *name;
> @@ -24,6 +26,7 @@ extern const struct proc_ns_operations ipcns_operations;
> extern const struct proc_ns_operations pidns_operations;
> extern const struct proc_ns_operations userns_operations;
> extern const struct proc_ns_operations mntns_operations;
> +extern const struct proc_ns_operations cgroupns_operations;
>
> /*
> * We always define these enumerators
> @@ -34,6 +37,7 @@ enum {
> PROC_UTS_INIT_INO = 0xEFFFFFFEU,
> PROC_USER_INIT_INO = 0xEFFFFFFDU,
> PROC_PID_INIT_INO = 0xEFFFFFFCU,
> + PROC_CGROUP_INIT_INO = 0xEFFFFFFBU,
> };
>
> #ifdef CONFIG_PROC_FS
> diff --git a/kernel/cgroup.c b/kernel/cgroup.c
> index 6b33631..60270b1 100644
> --- a/kernel/cgroup.c
> +++ b/kernel/cgroup.c
> @@ -57,6 +57,9 @@
> #include <linux/vmalloc.h> /* TODO: replace with more sophisticated array */
> #include <linux/kthread.h>
> #include <linux/delay.h>
> +#include <linux/proc_ns.h>
> +#include <linux/nsproxy.h>
> +#include <linux/proc_ns.h>
>
> #include <linux/atomic.h>
>
> @@ -208,6 +211,15 @@ static unsigned long have_fork_callback __read_mostly;
> static unsigned long have_exit_callback __read_mostly;
> static unsigned long have_free_callback __read_mostly;
>
> +/* Cgroup namespace for init task */
> +struct cgroup_namespace init_cgroup_ns = {
> + .count = { .counter = 2, },
> + .user_ns = &init_user_ns,
> + .ns.ops = &cgroupns_operations,
> + .ns.inum = PROC_CGROUP_INIT_INO,
> + .root_cset = &init_css_set,
> +};
> +
> /* Ditto for the can_fork callback. */
> static unsigned long have_canfork_callback __read_mostly;
>
> @@ -2166,6 +2178,43 @@ static struct file_system_type cgroup2_fs_type = {
> .kill_sb = cgroup_kill_sb,
> };
>
> +char *

Sorry, that one should be 'static char *'

> +cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen,
> + struct cgroup_namespace *ns)
> +{
> + int ret;
> + struct cgroup *root = cset_cgroup_from_root(ns->root_cset, cgrp->root);
> +
> + ret = kernfs_path_from_node(cgrp->kn, root->kn, buf, buflen);
> + if (ret < 0 || ret >= buflen)
> + return NULL;
> + return buf;
> +}
> +
> +char *cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
> + struct cgroup_namespace *ns)
> +{
> + char *ret;
> +
> + mutex_lock(&cgroup_mutex);
> + spin_lock_bh(&css_set_lock);
> +
> + ret = cgroup_path_ns_locked(cgrp, buf, buflen, ns);
> +
> + spin_unlock_bh(&css_set_lock);
> + mutex_unlock(&cgroup_mutex);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cgroup_path_ns);
> +
> +char *cgroup_path(struct cgroup *cgrp, char *buf, size_t buflen)
> +{
> + return cgroup_path_ns(cgrp, buf, buflen, &init_cgroup_ns);
> +}
> +
> +EXPORT_SYMBOL_GPL(cgroup_path);
> +
> /**
> * task_cgroup_path - cgroup path of a task in the first cgroup hierarchy
> * @task: target task
> @@ -2193,7 +2242,8 @@ char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
>
> if (root) {
> cgrp = task_cgroup_from_root(task, root);
> - path = cgroup_path(cgrp, buf, buflen);
> + path = cgroup_path_ns_locked(cgrp, buf, buflen,
> + &init_cgroup_ns);
> } else {
> /* if no hierarchy exists, everyone is in "/" */
> if (strlcpy(buf, "/", buflen) < buflen)
> @@ -5272,6 +5322,8 @@ int __init cgroup_init(void)
> BUG_ON(cgroup_init_cftypes(NULL, cgroup_dfl_base_files));
> BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files));
>
> + get_user_ns(init_cgroup_ns.user_ns);
> +
> mutex_lock(&cgroup_mutex);
>
> /* Add init_css_set to the hash table */
> @@ -5409,7 +5461,8 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
> * " (deleted)" is appended to the cgroup path.
> */
> if (cgroup_on_dfl(cgrp) || !(tsk->flags & PF_EXITING)) {
> - path = cgroup_path(cgrp, buf, PATH_MAX);
> + path = cgroup_path_ns_locked(cgrp, buf, PATH_MAX,
> + current->nsproxy->cgroup_ns);
> if (!path) {
> retval = -ENAMETOOLONG;
> goto out_unlock;
> @@ -5691,7 +5744,10 @@ static void cgroup_release_agent(struct work_struct *work)
> if (!pathbuf || !agentbuf)
> goto out;
>
> - path = cgroup_path(cgrp, pathbuf, PATH_MAX);
> + spin_lock_bh(&css_set_lock);
> + path = cgroup_path_ns_locked(cgrp, pathbuf, PATH_MAX,
> + &init_cgroup_ns);
> + spin_unlock_bh(&css_set_lock);
> if (!path)
> goto out;
>
> @@ -5822,6 +5878,121 @@ struct cgroup *cgroup_get_from_path(const char *path)
> }
> EXPORT_SYMBOL_GPL(cgroup_get_from_path);
>
> +/* cgroup namespaces */
> +
> +static struct cgroup_namespace *alloc_cgroup_ns(void)
> +{
> + struct cgroup_namespace *new_ns;
> + int ret;
> +
> + new_ns = kzalloc(sizeof(struct cgroup_namespace), GFP_KERNEL);
> + if (!new_ns)
> + return ERR_PTR(-ENOMEM);
> + ret = ns_alloc_inum(&new_ns->ns);
> + if (ret) {
> + kfree(new_ns);
> + return ERR_PTR(ret);
> + }
> + atomic_set(&new_ns->count, 1);
> + new_ns->ns.ops = &cgroupns_operations;
> + return new_ns;
> +}
> +
> +void free_cgroup_ns(struct cgroup_namespace *ns)
> +{
> + put_css_set(ns->root_cset);
> + put_user_ns(ns->user_ns);
> + ns_free_inum(&ns->ns);
> + kfree(ns);
> +}
> +EXPORT_SYMBOL(free_cgroup_ns);
> +
> +struct cgroup_namespace *
> +copy_cgroup_ns(unsigned long flags, struct user_namespace *user_ns,
> + struct cgroup_namespace *old_ns)
> +{
> + struct cgroup_namespace *new_ns = NULL;
> + struct css_set *cset = NULL;
> + int err;
> +
> + BUG_ON(!old_ns);
> +
> + if (!(flags & CLONE_NEWCGROUP)) {
> + get_cgroup_ns(old_ns);
> + return old_ns;
> + }
> +
> + /* Allow only sysadmin to create cgroup namespace. */
> + err = -EPERM;
> + if (!ns_capable(user_ns, CAP_SYS_ADMIN))
> + goto err_out;
> +
> + cset = task_css_set(current);
> + get_css_set(cset);
> +
> + err = -ENOMEM;
> + new_ns = alloc_cgroup_ns();
> + if (!new_ns)
> + goto err_out;
> +
> + new_ns->user_ns = get_user_ns(user_ns);
> + new_ns->root_cset = cset;
> +
> + return new_ns;
> +
> +err_out:
> + if (cset)
> + put_css_set(cset);
> + kfree(new_ns);
> + return ERR_PTR(err);
> +}
> +
> +static inline struct cgroup_namespace *to_cg_ns(struct ns_common *ns)
> +{
> + return container_of(ns, struct cgroup_namespace, ns);
> +}
> +
> +static int cgroupns_install(struct nsproxy *nsproxy, void *ns)
> +{
> + pr_info("setns not supported for cgroup namespace");
> + return -EINVAL;
> +}
> +
> +static struct ns_common *cgroupns_get(struct task_struct *task)
> +{
> + struct cgroup_namespace *ns = NULL;
> + struct nsproxy *nsproxy;
> +
> + task_lock(task);
> + nsproxy = task->nsproxy;
> + if (nsproxy) {
> + ns = nsproxy->cgroup_ns;
> + get_cgroup_ns(ns);
> + }
> + task_unlock(task);
> +
> + return ns ? &ns->ns : NULL;
> +}
> +
> +static void cgroupns_put(struct ns_common *ns)
> +{
> + put_cgroup_ns(to_cg_ns(ns));
> +}
> +
> +const struct proc_ns_operations cgroupns_operations = {
> + .name = "cgroup",
> + .type = CLONE_NEWCGROUP,
> + .get = cgroupns_get,
> + .put = cgroupns_put,
> + .install = cgroupns_install,
> +};
> +
> +static __init int cgroup_namespaces_init(void)
> +{
> + return 0;
> +}
> +subsys_initcall(cgroup_namespaces_init);
> +
> #ifdef CONFIG_CGROUP_DEBUG
> static struct cgroup_subsys_state *
> debug_css_alloc(struct cgroup_subsys_state *parent_css)
> diff --git a/kernel/cpuset.c b/kernel/cpuset.c
> index 3e945fc..37c8eb0 100644
> --- a/kernel/cpuset.c
> +++ b/kernel/cpuset.c
> @@ -2689,7 +2689,8 @@ int proc_cpuset_show(struct seq_file *m, struct pid_namespace *ns,
> retval = -ENAMETOOLONG;
> rcu_read_lock();
> css = task_css(tsk, cpuset_cgrp_id);
> - p = cgroup_path(css->cgroup, buf, PATH_MAX);
> + p = cgroup_path_ns(css->cgroup, buf, PATH_MAX,
> + current->nsproxy->cgroup_ns);
> rcu_read_unlock();
> if (!p)
> goto out_free;
> diff --git a/kernel/fork.c b/kernel/fork.c
> index ba7d1c0..7982fee 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -1880,7 +1880,7 @@ static int check_unshare_flags(unsigned long unshare_flags)
> if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
> CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
> CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET|
> - CLONE_NEWUSER|CLONE_NEWPID))
> + CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP))
> return -EINVAL;
> /*
> * Not implemented, but pretend it works if there is nothing
> diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
> index 49746c8..64fe865 100644
> --- a/kernel/nsproxy.c
> +++ b/kernel/nsproxy.c
> @@ -25,6 +25,7 @@
> #include <linux/proc_ns.h>
> #include <linux/file.h>
> #include <linux/syscalls.h>
> +#include <linux/cgroup.h>
>
> static struct kmem_cache *nsproxy_cachep;
>
> @@ -39,6 +40,9 @@ struct nsproxy init_nsproxy = {
> #ifdef CONFIG_NET
> .net_ns = &init_net,
> #endif
> +#ifdef CONFIG_CGROUPS
> + .cgroup_ns = &init_cgroup_ns,
> +#endif
> };
>
> static inline struct nsproxy *create_nsproxy(void)
> @@ -92,6 +96,13 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
> goto out_pid;
> }
>
> + new_nsp->cgroup_ns = copy_cgroup_ns(flags, user_ns,
> + tsk->nsproxy->cgroup_ns);
> + if (IS_ERR(new_nsp->cgroup_ns)) {
> + err = PTR_ERR(new_nsp->cgroup_ns);
> + goto out_cgroup;
> + }
> +
> new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns);
> if (IS_ERR(new_nsp->net_ns)) {
> err = PTR_ERR(new_nsp->net_ns);
> @@ -101,6 +112,9 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
> return new_nsp;
>
> out_net:
> + if (new_nsp->cgroup_ns)
> + put_cgroup_ns(new_nsp->cgroup_ns);
> +out_cgroup:
> if (new_nsp->pid_ns_for_children)
> put_pid_ns(new_nsp->pid_ns_for_children);
> out_pid:
> @@ -128,7 +142,8 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
> struct nsproxy *new_ns;
>
> if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
> - CLONE_NEWPID | CLONE_NEWNET)))) {
> + CLONE_NEWPID | CLONE_NEWNET |
> + CLONE_NEWCGROUP)))) {
> get_nsproxy(old_ns);
> return 0;
> }
> @@ -165,6 +180,8 @@ void free_nsproxy(struct nsproxy *ns)
> put_ipc_ns(ns->ipc_ns);
> if (ns->pid_ns_for_children)
> put_pid_ns(ns->pid_ns_for_children);
> + if (ns->cgroup_ns)
> + put_cgroup_ns(ns->cgroup_ns);
> put_net(ns->net_ns);
> kmem_cache_free(nsproxy_cachep, ns);
> }
> @@ -180,7 +197,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
> int err = 0;
>
> if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
> - CLONE_NEWNET | CLONE_NEWPID)))
> + CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP)))
> return 0;
>
> user_ns = new_cred ? new_cred->user_ns : current_user_ns();
> diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
> index 6415117..4c28523 100644
> --- a/kernel/sched/debug.c
> +++ b/kernel/sched/debug.c
> @@ -104,7 +104,8 @@ static char *task_group_path(struct task_group *tg)
> if (autogroup_path(tg, group_path, PATH_MAX))
> return group_path;
>
> - return cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
> + return cgroup_path_ns(tg->css.cgroup, group_path, PATH_MAX,
> + current->nsproxy->cgroup_ns);
> }
> #endif
>
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/