cgroup: move namespace code to kernel/cgroup/namespace.c
get/put_css_set() get exposed in cgroup-internal.h in the process. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Acked-by: Zefan Li <lizefan@huawei.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
obj-y := cgroup.o cgroup-v1.o
|
obj-y := cgroup.o namespace.o cgroup-v1.o
|
||||||
|
|
||||||
obj-$(CONFIG_CGROUP_FREEZER) += freezer.o
|
obj-$(CONFIG_CGROUP_FREEZER) += freezer.o
|
||||||
obj-$(CONFIG_CGROUP_PIDS) += pids.o
|
obj-$(CONFIG_CGROUP_PIDS) += pids.o
|
||||||
|
|||||||
@@ -65,6 +65,33 @@ static inline bool notify_on_release(const struct cgroup *cgrp)
|
|||||||
return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
|
return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void put_css_set_locked(struct css_set *cset);
|
||||||
|
|
||||||
|
static inline void put_css_set(struct css_set *cset)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that the refcount doesn't hit zero while any readers
|
||||||
|
* can see it. Similar to atomic_dec_and_lock(), but for an
|
||||||
|
* rwlock
|
||||||
|
*/
|
||||||
|
if (atomic_add_unless(&cset->refcount, -1, 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&css_set_lock, flags);
|
||||||
|
put_css_set_locked(cset);
|
||||||
|
spin_unlock_irqrestore(&css_set_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* refcounted get/put for css_set objects
|
||||||
|
*/
|
||||||
|
static inline void get_css_set(struct css_set *cset)
|
||||||
|
{
|
||||||
|
atomic_inc(&cset->refcount);
|
||||||
|
}
|
||||||
|
|
||||||
bool cgroup_ssid_enabled(int ssid);
|
bool cgroup_ssid_enabled(int ssid);
|
||||||
bool cgroup_on_dfl(const struct cgroup *cgrp);
|
bool cgroup_on_dfl(const struct cgroup *cgrp);
|
||||||
|
|
||||||
@@ -107,6 +134,11 @@ int cgroup_rmdir(struct kernfs_node *kn);
|
|||||||
int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,
|
int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,
|
||||||
struct kernfs_root *kf_root);
|
struct kernfs_root *kf_root);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* namespace.c
|
||||||
|
*/
|
||||||
|
extern const struct proc_ns_operations cgroupns_operations;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cgroup-v1.c
|
* cgroup-v1.c
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -718,7 +718,7 @@ static unsigned long css_set_hash(struct cgroup_subsys_state *css[])
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void put_css_set_locked(struct css_set *cset)
|
void put_css_set_locked(struct css_set *cset)
|
||||||
{
|
{
|
||||||
struct cgrp_cset_link *link, *tmp_link;
|
struct cgrp_cset_link *link, *tmp_link;
|
||||||
struct cgroup_subsys *ss;
|
struct cgroup_subsys *ss;
|
||||||
@@ -748,31 +748,6 @@ static void put_css_set_locked(struct css_set *cset)
|
|||||||
kfree_rcu(cset, rcu_head);
|
kfree_rcu(cset, rcu_head);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void put_css_set(struct css_set *cset)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensure that the refcount doesn't hit zero while any readers
|
|
||||||
* can see it. Similar to atomic_dec_and_lock(), but for an
|
|
||||||
* rwlock
|
|
||||||
*/
|
|
||||||
if (atomic_add_unless(&cset->refcount, -1, 1))
|
|
||||||
return;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&css_set_lock, flags);
|
|
||||||
put_css_set_locked(cset);
|
|
||||||
spin_unlock_irqrestore(&css_set_lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* refcounted get/put for css_set objects
|
|
||||||
*/
|
|
||||||
static inline void get_css_set(struct css_set *cset)
|
|
||||||
{
|
|
||||||
atomic_inc(&cset->refcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* compare_css_sets - helper function for find_existing_css_set().
|
* compare_css_sets - helper function for find_existing_css_set().
|
||||||
* @cset: candidate css_set being tested
|
* @cset: candidate css_set being tested
|
||||||
@@ -5109,154 +5084,6 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd)
|
|||||||
|
|
||||||
#endif /* CONFIG_SOCK_CGROUP_DATA */
|
#endif /* CONFIG_SOCK_CGROUP_DATA */
|
||||||
|
|
||||||
/* cgroup namespaces */
|
|
||||||
|
|
||||||
static struct ucounts *inc_cgroup_namespaces(struct user_namespace *ns)
|
|
||||||
{
|
|
||||||
return inc_ucount(ns, current_euid(), UCOUNT_CGROUP_NAMESPACES);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dec_cgroup_namespaces(struct ucounts *ucounts)
|
|
||||||
{
|
|
||||||
dec_ucount(ucounts, UCOUNT_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);
|
|
||||||
dec_cgroup_namespaces(ns->ucounts);
|
|
||||||
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;
|
|
||||||
struct ucounts *ucounts;
|
|
||||||
struct css_set *cset;
|
|
||||||
|
|
||||||
BUG_ON(!old_ns);
|
|
||||||
|
|
||||||
if (!(flags & CLONE_NEWCGROUP)) {
|
|
||||||
get_cgroup_ns(old_ns);
|
|
||||||
return old_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allow only sysadmin to create cgroup namespace. */
|
|
||||||
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
|
|
||||||
return ERR_PTR(-EPERM);
|
|
||||||
|
|
||||||
ucounts = inc_cgroup_namespaces(user_ns);
|
|
||||||
if (!ucounts)
|
|
||||||
return ERR_PTR(-ENOSPC);
|
|
||||||
|
|
||||||
/* It is not safe to take cgroup_mutex here */
|
|
||||||
spin_lock_irq(&css_set_lock);
|
|
||||||
cset = task_css_set(current);
|
|
||||||
get_css_set(cset);
|
|
||||||
spin_unlock_irq(&css_set_lock);
|
|
||||||
|
|
||||||
new_ns = alloc_cgroup_ns();
|
|
||||||
if (IS_ERR(new_ns)) {
|
|
||||||
put_css_set(cset);
|
|
||||||
dec_cgroup_namespaces(ucounts);
|
|
||||||
return new_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_ns->user_ns = get_user_ns(user_ns);
|
|
||||||
new_ns->ucounts = ucounts;
|
|
||||||
new_ns->root_cset = cset;
|
|
||||||
|
|
||||||
return new_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, struct ns_common *ns)
|
|
||||||
{
|
|
||||||
struct cgroup_namespace *cgroup_ns = to_cg_ns(ns);
|
|
||||||
|
|
||||||
if (!ns_capable(current_user_ns(), CAP_SYS_ADMIN) ||
|
|
||||||
!ns_capable(cgroup_ns->user_ns, CAP_SYS_ADMIN))
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
/* Don't need to do anything if we are attaching to our own cgroupns. */
|
|
||||||
if (cgroup_ns == nsproxy->cgroup_ns)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
get_cgroup_ns(cgroup_ns);
|
|
||||||
put_cgroup_ns(nsproxy->cgroup_ns);
|
|
||||||
nsproxy->cgroup_ns = cgroup_ns;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct user_namespace *cgroupns_owner(struct ns_common *ns)
|
|
||||||
{
|
|
||||||
return to_cg_ns(ns)->user_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct proc_ns_operations cgroupns_operations = {
|
|
||||||
.name = "cgroup",
|
|
||||||
.type = CLONE_NEWCGROUP,
|
|
||||||
.get = cgroupns_get,
|
|
||||||
.put = cgroupns_put,
|
|
||||||
.install = cgroupns_install,
|
|
||||||
.owner = cgroupns_owner,
|
|
||||||
};
|
|
||||||
|
|
||||||
static __init int cgroup_namespaces_init(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
subsys_initcall(cgroup_namespaces_init);
|
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_BPF
|
#ifdef CONFIG_CGROUP_BPF
|
||||||
void cgroup_bpf_update(struct cgroup *cgrp,
|
void cgroup_bpf_update(struct cgroup *cgrp,
|
||||||
struct bpf_prog *prog,
|
struct bpf_prog *prog,
|
||||||
|
|||||||
155
kernel/cgroup/namespace.c
Normal file
155
kernel/cgroup/namespace.c
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#include "cgroup-internal.h"
|
||||||
|
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/proc_ns.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* cgroup namespaces */
|
||||||
|
|
||||||
|
static struct ucounts *inc_cgroup_namespaces(struct user_namespace *ns)
|
||||||
|
{
|
||||||
|
return inc_ucount(ns, current_euid(), UCOUNT_CGROUP_NAMESPACES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dec_cgroup_namespaces(struct ucounts *ucounts)
|
||||||
|
{
|
||||||
|
dec_ucount(ucounts, UCOUNT_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);
|
||||||
|
dec_cgroup_namespaces(ns->ucounts);
|
||||||
|
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;
|
||||||
|
struct ucounts *ucounts;
|
||||||
|
struct css_set *cset;
|
||||||
|
|
||||||
|
BUG_ON(!old_ns);
|
||||||
|
|
||||||
|
if (!(flags & CLONE_NEWCGROUP)) {
|
||||||
|
get_cgroup_ns(old_ns);
|
||||||
|
return old_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allow only sysadmin to create cgroup namespace. */
|
||||||
|
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
|
||||||
|
return ERR_PTR(-EPERM);
|
||||||
|
|
||||||
|
ucounts = inc_cgroup_namespaces(user_ns);
|
||||||
|
if (!ucounts)
|
||||||
|
return ERR_PTR(-ENOSPC);
|
||||||
|
|
||||||
|
/* It is not safe to take cgroup_mutex here */
|
||||||
|
spin_lock_irq(&css_set_lock);
|
||||||
|
cset = task_css_set(current);
|
||||||
|
get_css_set(cset);
|
||||||
|
spin_unlock_irq(&css_set_lock);
|
||||||
|
|
||||||
|
new_ns = alloc_cgroup_ns();
|
||||||
|
if (IS_ERR(new_ns)) {
|
||||||
|
put_css_set(cset);
|
||||||
|
dec_cgroup_namespaces(ucounts);
|
||||||
|
return new_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_ns->user_ns = get_user_ns(user_ns);
|
||||||
|
new_ns->ucounts = ucounts;
|
||||||
|
new_ns->root_cset = cset;
|
||||||
|
|
||||||
|
return new_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, struct ns_common *ns)
|
||||||
|
{
|
||||||
|
struct cgroup_namespace *cgroup_ns = to_cg_ns(ns);
|
||||||
|
|
||||||
|
if (!ns_capable(current_user_ns(), CAP_SYS_ADMIN) ||
|
||||||
|
!ns_capable(cgroup_ns->user_ns, CAP_SYS_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
/* Don't need to do anything if we are attaching to our own cgroupns. */
|
||||||
|
if (cgroup_ns == nsproxy->cgroup_ns)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
get_cgroup_ns(cgroup_ns);
|
||||||
|
put_cgroup_ns(nsproxy->cgroup_ns);
|
||||||
|
nsproxy->cgroup_ns = cgroup_ns;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct user_namespace *cgroupns_owner(struct ns_common *ns)
|
||||||
|
{
|
||||||
|
return to_cg_ns(ns)->user_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct proc_ns_operations cgroupns_operations = {
|
||||||
|
.name = "cgroup",
|
||||||
|
.type = CLONE_NEWCGROUP,
|
||||||
|
.get = cgroupns_get,
|
||||||
|
.put = cgroupns_put,
|
||||||
|
.install = cgroupns_install,
|
||||||
|
.owner = cgroupns_owner,
|
||||||
|
};
|
||||||
|
|
||||||
|
static __init int cgroup_namespaces_init(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
subsys_initcall(cgroup_namespaces_init);
|
||||||
Reference in New Issue
Block a user