Commit 6f60eade authored by Tejun Heo's avatar Tejun Heo

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: default avatarTejun Heo <tj@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
parent 4df8dc90
...@@ -83,6 +83,17 @@ enum { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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[] = { ...@@ -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) ...@@ -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, ...@@ -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)) {
......
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