Commit b11cfb58 authored by Tejun Heo's avatar Tejun Heo

cgroup: record ancestor IDs and reimplement cgroup_is_descendant() using it

cgroup_is_descendant() currently walks up the hierarchy and compares
each ancestor to the cgroup in question.  While enough for cgroup core
usages, this can't be used in hot paths to test cgroup membership.
This patch adds cgroup->ancestor_ids[] which records the IDs of all
ancestors including self and cgroup->level for the nesting level.

This allows testing whether a given cgroup is a descendant of another
in three finite steps - testing whether the two belong to the same
hierarchy, whether the descendant candidate is at the same or a higher
level than the ancestor and comparing the recorded ancestor_id at the
matching level.  cgroup_is_descendant() is accordingly reimplmented
and made inline.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent 8005c49d
...@@ -234,6 +234,14 @@ struct cgroup { ...@@ -234,6 +234,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
...@@ -289,6 +297,9 @@ struct cgroup { ...@@ -289,6 +297,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[];
}; };
/* /*
...@@ -308,6 +319,9 @@ struct cgroup_root { ...@@ -308,6 +319,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;
......
...@@ -81,7 +81,6 @@ struct cgroup_subsys_state *cgroup_get_e_css(struct cgroup *cgroup, ...@@ -81,7 +81,6 @@ 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);
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);
...@@ -459,6 +458,23 @@ static inline struct cgroup *task_cgroup(struct task_struct *task, ...@@ -459,6 +458,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)
{ {
......
...@@ -459,25 +459,6 @@ struct cgroup_subsys_state *of_css(struct kernfs_open_file *of) ...@@ -459,25 +459,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);
...@@ -1903,6 +1884,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask) ...@@ -1903,6 +1884,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);
...@@ -4846,11 +4828,11 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss, ...@@ -4846,11 +4828,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.
*/ */
...@@ -4861,9 +4843,11 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, ...@@ -4861,9 +4843,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;
...@@ -4887,6 +4871,10 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, ...@@ -4887,6 +4871,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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment