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: Tejun Heo <tj@kernel.org>
This commit is contained in:
parent
8005c49d9a
commit
b11cfb5807
|
@ -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 {
|
||||||
|
|
||||||
/* 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 {
|
||||||
/* 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,
|
||||||
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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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 @@ err_free_css:
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue