cgroup: generalize obtaining the handles of and notifying cgroup files
cgroup core handles creations and removals of cgroup interface files as described by cftypes. There are cases where the handle for a given file instance is necessary, for example, to generate a file modified event. Currently, this is handled by explicitly matching the callback method pointer and storing the file handle manually in cgroup_add_file(). While this simple approach works for cgroup core files, it can't for controller interface files. This patch generalizes cgroup interface file handle handling. struct cgroup_file is defined and each cftype can optionally tell cgroup core to store the file handle by setting ->file_offset. A file handle remains accessible as long as the containing css is accessible. Both "cgroup.procs" and "cgroup.events" are converted to use the new generic mechanism instead of hooking directly into cgroup_add_file(). Also, cgroup_file_notify() which takes a struct cgroup_file and generates a file modified event on it is added and replaces explicit kernfs_notify() invocations. This generalizes cgroup file handle handling and allows controllers to generate file modified notifications. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Li Zefan <lizefan@huawei.com> Cc: Johannes Weiner <hannes@cmpxchg.org>
This commit is contained in:
Родитель
4df8dc9031
Коммит
6f60eade24
|
@ -83,6 +83,17 @@ enum {
|
||||||
__CFTYPE_NOT_ON_DFL = (1 << 17), /* not on default hierarchy */
|
__CFTYPE_NOT_ON_DFL = (1 << 17), /* not on default hierarchy */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cgroup_file is the handle for a file instance created in a cgroup which
|
||||||
|
* is used, for example, to generate file changed notifications. This can
|
||||||
|
* be obtained by setting cftype->file_offset.
|
||||||
|
*/
|
||||||
|
struct cgroup_file {
|
||||||
|
/* do not access any fields from outside cgroup core */
|
||||||
|
struct list_head node; /* anchored at css->files */
|
||||||
|
struct kernfs_node *kn;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per-subsystem/per-cgroup state maintained by the system. This is the
|
* Per-subsystem/per-cgroup state maintained by the system. This is the
|
||||||
* fundamental structural building block that controllers deal with.
|
* fundamental structural building block that controllers deal with.
|
||||||
|
@ -123,6 +134,9 @@ struct cgroup_subsys_state {
|
||||||
*/
|
*/
|
||||||
u64 serial_nr;
|
u64 serial_nr;
|
||||||
|
|
||||||
|
/* all cgroup_files associated with this css */
|
||||||
|
struct list_head files;
|
||||||
|
|
||||||
/* percpu_ref killing and RCU release */
|
/* percpu_ref killing and RCU release */
|
||||||
struct rcu_head rcu_head;
|
struct rcu_head rcu_head;
|
||||||
struct work_struct destroy_work;
|
struct work_struct destroy_work;
|
||||||
|
@ -226,8 +240,8 @@ struct cgroup {
|
||||||
int populated_cnt;
|
int populated_cnt;
|
||||||
|
|
||||||
struct kernfs_node *kn; /* cgroup kernfs entry */
|
struct kernfs_node *kn; /* cgroup kernfs entry */
|
||||||
struct kernfs_node *procs_kn; /* kn for "cgroup.procs" */
|
struct cgroup_file procs_file; /* handle for "cgroup.procs" */
|
||||||
struct kernfs_node *events_kn; /* kn for "cgroup.events" */
|
struct cgroup_file events_file; /* handle for "cgroup.events" */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The bitmask of subsystems enabled on the child cgroups.
|
* The bitmask of subsystems enabled on the child cgroups.
|
||||||
|
@ -335,6 +349,14 @@ struct cftype {
|
||||||
/* CFTYPE_* flags */
|
/* CFTYPE_* flags */
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If non-zero, should contain the offset from the start of css to
|
||||||
|
* a struct cgroup_file field. cgroup will record the handle of
|
||||||
|
* the created file into it. The recorded handle can be used as
|
||||||
|
* long as the containing css remains accessible.
|
||||||
|
*/
|
||||||
|
unsigned int file_offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fields used for internal bookkeeping. Initialized automatically
|
* Fields used for internal bookkeeping. Initialized automatically
|
||||||
* during registration.
|
* during registration.
|
||||||
|
|
|
@ -490,6 +490,19 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp)
|
||||||
pr_cont_kernfs_path(cgrp->kn);
|
pr_cont_kernfs_path(cgrp->kn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cgroup_file_notify - generate a file modified event for a cgroup_file
|
||||||
|
* @cfile: target cgroup_file
|
||||||
|
*
|
||||||
|
* @cfile must have been obtained by setting cftype->file_offset.
|
||||||
|
*/
|
||||||
|
static inline void cgroup_file_notify(struct cgroup_file *cfile)
|
||||||
|
{
|
||||||
|
/* might not have been created due to one of the CFTYPE selector flags */
|
||||||
|
if (cfile->kn)
|
||||||
|
kernfs_notify(cfile->kn);
|
||||||
|
}
|
||||||
|
|
||||||
#else /* !CONFIG_CGROUPS */
|
#else /* !CONFIG_CGROUPS */
|
||||||
|
|
||||||
struct cgroup_subsys_state;
|
struct cgroup_subsys_state;
|
||||||
|
|
|
@ -612,8 +612,8 @@ static void cgroup_update_populated(struct cgroup *cgrp, bool populated)
|
||||||
if (!trigger)
|
if (!trigger)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (cgrp->events_kn)
|
cgroup_file_notify(&cgrp->events_file);
|
||||||
kernfs_notify(cgrp->events_kn);
|
|
||||||
cgrp = cgroup_parent(cgrp);
|
cgrp = cgroup_parent(cgrp);
|
||||||
} while (cgrp);
|
} while (cgrp);
|
||||||
}
|
}
|
||||||
|
@ -1771,6 +1771,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
|
||||||
|
|
||||||
INIT_LIST_HEAD(&cgrp->self.sibling);
|
INIT_LIST_HEAD(&cgrp->self.sibling);
|
||||||
INIT_LIST_HEAD(&cgrp->self.children);
|
INIT_LIST_HEAD(&cgrp->self.children);
|
||||||
|
INIT_LIST_HEAD(&cgrp->self.files);
|
||||||
INIT_LIST_HEAD(&cgrp->cset_links);
|
INIT_LIST_HEAD(&cgrp->cset_links);
|
||||||
INIT_LIST_HEAD(&cgrp->pidlists);
|
INIT_LIST_HEAD(&cgrp->pidlists);
|
||||||
mutex_init(&cgrp->pidlist_mutex);
|
mutex_init(&cgrp->pidlist_mutex);
|
||||||
|
@ -2562,7 +2563,7 @@ static int cgroup_procs_write_permission(struct task_struct *task,
|
||||||
cgrp = cgroup_parent(cgrp);
|
cgrp = cgroup_parent(cgrp);
|
||||||
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
inode = kernfs_get_inode(sb, cgrp->procs_kn);
|
inode = kernfs_get_inode(sb, cgrp->procs_file.kn);
|
||||||
if (inode) {
|
if (inode) {
|
||||||
ret = inode_permission(inode, MAY_WRITE);
|
ret = inode_permission(inode, MAY_WRITE);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
|
@ -3253,10 +3254,14 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cft->write == cgroup_procs_write)
|
if (cft->file_offset) {
|
||||||
cgrp->procs_kn = kn;
|
struct cgroup_file *cfile = (void *)css + cft->file_offset;
|
||||||
else if (cft->seq_show == cgroup_events_show)
|
|
||||||
cgrp->events_kn = kn;
|
kernfs_get(kn);
|
||||||
|
cfile->kn = kn;
|
||||||
|
list_add(&cfile->node, &css->files);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4408,6 +4413,7 @@ static int cgroup_clone_children_write(struct cgroup_subsys_state *css,
|
||||||
static struct cftype cgroup_dfl_base_files[] = {
|
static struct cftype cgroup_dfl_base_files[] = {
|
||||||
{
|
{
|
||||||
.name = "cgroup.procs",
|
.name = "cgroup.procs",
|
||||||
|
.file_offset = offsetof(struct cgroup, procs_file),
|
||||||
.seq_start = cgroup_pidlist_start,
|
.seq_start = cgroup_pidlist_start,
|
||||||
.seq_next = cgroup_pidlist_next,
|
.seq_next = cgroup_pidlist_next,
|
||||||
.seq_stop = cgroup_pidlist_stop,
|
.seq_stop = cgroup_pidlist_stop,
|
||||||
|
@ -4433,6 +4439,7 @@ static struct cftype cgroup_dfl_base_files[] = {
|
||||||
{
|
{
|
||||||
.name = "cgroup.events",
|
.name = "cgroup.events",
|
||||||
.flags = CFTYPE_NOT_ON_ROOT,
|
.flags = CFTYPE_NOT_ON_ROOT,
|
||||||
|
.file_offset = offsetof(struct cgroup, events_file),
|
||||||
.seq_show = cgroup_events_show,
|
.seq_show = cgroup_events_show,
|
||||||
},
|
},
|
||||||
{ } /* terminate */
|
{ } /* terminate */
|
||||||
|
@ -4511,9 +4518,13 @@ static void css_free_work_fn(struct work_struct *work)
|
||||||
container_of(work, struct cgroup_subsys_state, destroy_work);
|
container_of(work, struct cgroup_subsys_state, destroy_work);
|
||||||
struct cgroup_subsys *ss = css->ss;
|
struct cgroup_subsys *ss = css->ss;
|
||||||
struct cgroup *cgrp = css->cgroup;
|
struct cgroup *cgrp = css->cgroup;
|
||||||
|
struct cgroup_file *cfile;
|
||||||
|
|
||||||
percpu_ref_exit(&css->refcnt);
|
percpu_ref_exit(&css->refcnt);
|
||||||
|
|
||||||
|
list_for_each_entry(cfile, &css->files, node)
|
||||||
|
kernfs_put(cfile->kn);
|
||||||
|
|
||||||
if (ss) {
|
if (ss) {
|
||||||
/* css free path */
|
/* css free path */
|
||||||
int id = css->id;
|
int id = css->id;
|
||||||
|
@ -4618,6 +4629,7 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
|
||||||
css->ss = ss;
|
css->ss = ss;
|
||||||
INIT_LIST_HEAD(&css->sibling);
|
INIT_LIST_HEAD(&css->sibling);
|
||||||
INIT_LIST_HEAD(&css->children);
|
INIT_LIST_HEAD(&css->children);
|
||||||
|
INIT_LIST_HEAD(&css->files);
|
||||||
css->serial_nr = css_serial_nr_next++;
|
css->serial_nr = css_serial_nr_next++;
|
||||||
|
|
||||||
if (cgroup_parent(cgrp)) {
|
if (cgroup_parent(cgrp)) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче