Merge branch 'for-4.5-ancestor-test' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup into for-4.5

Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Tejun Heo 2015-12-07 17:24:10 -05:00
commit 177493987c
5 changed files with 140 additions and 28 deletions

View File

@ -694,6 +694,29 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
return NULL; return NULL;
} }
static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
const unsigned char *path,
const void *ns)
{
static char path_buf[PATH_MAX]; /* protected by kernfs_mutex */
size_t len = strlcpy(path_buf, path, PATH_MAX);
char *p = path_buf;
char *name;
lockdep_assert_held(&kernfs_mutex);
if (len >= PATH_MAX)
return NULL;
while ((name = strsep(&p, "/")) && parent) {
if (*name == '\0')
continue;
parent = kernfs_find_ns(parent, name, ns);
}
return parent;
}
/** /**
* kernfs_find_and_get_ns - find and get kernfs_node with the given name * kernfs_find_and_get_ns - find and get kernfs_node with the given name
* @parent: kernfs_node to search under * @parent: kernfs_node to search under
@ -718,6 +741,29 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
} }
EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
/**
* kernfs_walk_and_get_ns - find and get kernfs_node with the given path
* @parent: kernfs_node to search under
* @path: path to look for
* @ns: the namespace tag to use
*
* Look for kernfs_node with path @path under @parent and get a reference
* if found. This function may sleep and returns pointer to the found
* kernfs_node on success, %NULL on failure.
*/
struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
const char *path, const void *ns)
{
struct kernfs_node *kn;
mutex_lock(&kernfs_mutex);
kn = kernfs_walk_ns(parent, path, ns);
kernfs_get(kn);
mutex_unlock(&kernfs_mutex);
return kn;
}
/** /**
* kernfs_create_root - create a new kernfs hierarchy * kernfs_create_root - create a new kernfs hierarchy
* @scops: optional syscall operations for the hierarchy * @scops: optional syscall operations for the hierarchy

View File

@ -224,6 +224,14 @@ struct cgroup {
*/ */
int id; int id;
/*
* The depth this cgroup is at. The root is at depth zero and each
* step down the hierarchy increments the level. This along with
* ancestor_ids[] can determine whether a given cgroup is a
* descendant of another without traversing the hierarchy.
*/
int level;
/* /*
* Each non-empty css_set associated with this cgroup contributes * Each non-empty css_set associated with this cgroup contributes
* one to populated_cnt. All children with non-zero popuplated_cnt * one to populated_cnt. All children with non-zero popuplated_cnt
@ -279,6 +287,9 @@ struct cgroup {
/* used to schedule release agent */ /* used to schedule release agent */
struct work_struct release_agent_work; struct work_struct release_agent_work;
/* ids of the ancestors at each level including self */
int ancestor_ids[];
}; };
/* /*
@ -298,6 +309,9 @@ struct cgroup_root {
/* The root cgroup. Root is destroyed on its release. */ /* The root cgroup. Root is destroyed on its release. */
struct cgroup cgrp; struct cgroup cgrp;
/* for cgrp->ancestor_ids[0] */
int cgrp_ancestor_id_storage;
/* Number of cgroups in the hierarchy, used only for /proc/cgroups */ /* Number of cgroups in the hierarchy, used only for /proc/cgroups */
atomic_t nr_cgrps; atomic_t nr_cgrps;

View File

@ -81,7 +81,8 @@ struct cgroup_subsys_state *cgroup_get_e_css(struct cgroup *cgroup,
struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry, struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry,
struct cgroup_subsys *ss); struct cgroup_subsys *ss);
bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor); struct cgroup *cgroup_get_from_path(const char *path);
int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); int cgroup_attach_task_all(struct task_struct *from, struct task_struct *);
int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from); int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from);
@ -361,6 +362,11 @@ static inline void css_put_many(struct cgroup_subsys_state *css, unsigned int n)
percpu_ref_put_many(&css->refcnt, n); percpu_ref_put_many(&css->refcnt, n);
} }
static inline void cgroup_put(struct cgroup *cgrp)
{
css_put(&cgrp->self);
}
/** /**
* task_css_set_check - obtain a task's css_set with extra access conditions * task_css_set_check - obtain a task's css_set with extra access conditions
* @task: the task to obtain css_set for * @task: the task to obtain css_set for
@ -468,6 +474,23 @@ static inline struct cgroup *task_cgroup(struct task_struct *task,
return task_css(task, subsys_id)->cgroup; return task_css(task, subsys_id)->cgroup;
} }
/**
* cgroup_is_descendant - test ancestry
* @cgrp: the cgroup to be tested
* @ancestor: possible ancestor of @cgrp
*
* Test whether @cgrp is a descendant of @ancestor. It also returns %true
* if @cgrp == @ancestor. This function is safe to call as long as @cgrp
* and @ancestor are accessible.
*/
static inline bool cgroup_is_descendant(struct cgroup *cgrp,
struct cgroup *ancestor)
{
if (cgrp->root != ancestor->root || cgrp->level < ancestor->level)
return false;
return cgrp->ancestor_ids[ancestor->level] == ancestor->id;
}
/* no synchronization, the result can only be used as a hint */ /* no synchronization, the result can only be used as a hint */
static inline bool cgroup_is_populated(struct cgroup *cgrp) static inline bool cgroup_is_populated(struct cgroup *cgrp)
{ {

View File

@ -274,6 +274,8 @@ void pr_cont_kernfs_path(struct kernfs_node *kn);
struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn); struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn);
struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
const char *name, const void *ns); const char *name, const void *ns);
struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
const char *path, const void *ns);
void kernfs_get(struct kernfs_node *kn); void kernfs_get(struct kernfs_node *kn);
void kernfs_put(struct kernfs_node *kn); void kernfs_put(struct kernfs_node *kn);
@ -350,6 +352,10 @@ static inline struct kernfs_node *
kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name,
const void *ns) const void *ns)
{ return NULL; } { return NULL; }
static inline struct kernfs_node *
kernfs_walk_and_get_ns(struct kernfs_node *parent, const char *path,
const void *ns)
{ return NULL; }
static inline void kernfs_get(struct kernfs_node *kn) { } static inline void kernfs_get(struct kernfs_node *kn) { }
static inline void kernfs_put(struct kernfs_node *kn) { } static inline void kernfs_put(struct kernfs_node *kn) { }
@ -430,6 +436,12 @@ kernfs_find_and_get(struct kernfs_node *kn, const char *name)
return kernfs_find_and_get_ns(kn, name, NULL); return kernfs_find_and_get_ns(kn, name, NULL);
} }
static inline struct kernfs_node *
kernfs_walk_and_get(struct kernfs_node *kn, const char *path)
{
return kernfs_walk_and_get_ns(kn, path, NULL);
}
static inline struct kernfs_node * static inline struct kernfs_node *
kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode, kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode,
void *priv) void *priv)

View File

@ -441,11 +441,6 @@ static bool cgroup_tryget(struct cgroup *cgrp)
return css_tryget(&cgrp->self); return css_tryget(&cgrp->self);
} }
static void cgroup_put(struct cgroup *cgrp)
{
css_put(&cgrp->self);
}
struct cgroup_subsys_state *of_css(struct kernfs_open_file *of) struct cgroup_subsys_state *of_css(struct kernfs_open_file *of)
{ {
struct cgroup *cgrp = of->kn->parent->priv; struct cgroup *cgrp = of->kn->parent->priv;
@ -466,25 +461,6 @@ struct cgroup_subsys_state *of_css(struct kernfs_open_file *of)
} }
EXPORT_SYMBOL_GPL(of_css); EXPORT_SYMBOL_GPL(of_css);
/**
* cgroup_is_descendant - test ancestry
* @cgrp: the cgroup to be tested
* @ancestor: possible ancestor of @cgrp
*
* Test whether @cgrp is a descendant of @ancestor. It also returns %true
* if @cgrp == @ancestor. This function is safe to call as long as @cgrp
* and @ancestor are accessible.
*/
bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor)
{
while (cgrp) {
if (cgrp == ancestor)
return true;
cgrp = cgroup_parent(cgrp);
}
return false;
}
static int notify_on_release(const struct cgroup *cgrp) static int 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);
@ -1912,6 +1888,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask)
if (ret < 0) if (ret < 0)
goto out; goto out;
root_cgrp->id = ret; root_cgrp->id = ret;
root_cgrp->ancestor_ids[0] = ret;
ret = percpu_ref_init(&root_cgrp->self.refcnt, css_release, 0, ret = percpu_ref_init(&root_cgrp->self.refcnt, css_release, 0,
GFP_KERNEL); GFP_KERNEL);
@ -4901,11 +4878,11 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss,
static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
umode_t mode) umode_t mode)
{ {
struct cgroup *parent, *cgrp; struct cgroup *parent, *cgrp, *tcgrp;
struct cgroup_root *root; struct cgroup_root *root;
struct cgroup_subsys *ss; struct cgroup_subsys *ss;
struct kernfs_node *kn; struct kernfs_node *kn;
int ssid, ret; int level, ssid, ret;
/* Do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable. /* Do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable.
*/ */
@ -4916,9 +4893,11 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
if (!parent) if (!parent)
return -ENODEV; return -ENODEV;
root = parent->root; root = parent->root;
level = parent->level + 1;
/* allocate the cgroup and its ID, 0 is reserved for the root */ /* allocate the cgroup and its ID, 0 is reserved for the root */
cgrp = kzalloc(sizeof(*cgrp), GFP_KERNEL); cgrp = kzalloc(sizeof(*cgrp) +
sizeof(cgrp->ancestor_ids[0]) * (level + 1), GFP_KERNEL);
if (!cgrp) { if (!cgrp) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_unlock; goto out_unlock;
@ -4942,6 +4921,10 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
cgrp->self.parent = &parent->self; cgrp->self.parent = &parent->self;
cgrp->root = root; cgrp->root = root;
cgrp->level = level;
for (tcgrp = cgrp; tcgrp; tcgrp = cgroup_parent(tcgrp))
cgrp->ancestor_ids[tcgrp->level] = tcgrp->id;
if (notify_on_release(parent)) if (notify_on_release(parent))
set_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); set_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
@ -5805,6 +5788,40 @@ struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss)
return id > 0 ? idr_find(&ss->css_idr, id) : NULL; return id > 0 ? idr_find(&ss->css_idr, id) : NULL;
} }
/**
* cgroup_get_from_path - lookup and get a cgroup from its default hierarchy path
* @path: path on the default hierarchy
*
* Find the cgroup at @path on the default hierarchy, increment its
* reference count and return it. Returns pointer to the found cgroup on
* success, ERR_PTR(-ENOENT) if @path doens't exist and ERR_PTR(-ENOTDIR)
* if @path points to a non-directory.
*/
struct cgroup *cgroup_get_from_path(const char *path)
{
struct kernfs_node *kn;
struct cgroup *cgrp;
mutex_lock(&cgroup_mutex);
kn = kernfs_walk_and_get(cgrp_dfl_root.cgrp.kn, path);
if (kn) {
if (kernfs_type(kn) == KERNFS_DIR) {
cgrp = kn->priv;
cgroup_get(cgrp);
} else {
cgrp = ERR_PTR(-ENOTDIR);
}
kernfs_put(kn);
} else {
cgrp = ERR_PTR(-ENOENT);
}
mutex_unlock(&cgroup_mutex);
return cgrp;
}
EXPORT_SYMBOL_GPL(cgroup_get_from_path);
#ifdef CONFIG_CGROUP_DEBUG #ifdef CONFIG_CGROUP_DEBUG
static struct cgroup_subsys_state * static struct cgroup_subsys_state *
debug_css_alloc(struct cgroup_subsys_state *parent_css) debug_css_alloc(struct cgroup_subsys_state *parent_css)