cgroup: generalize obtaining the handles of and notifying cgroup files
authorTejun Heo <tj@kernel.org>
Fri, 18 Sep 2015 21:54:23 +0000 (17:54 -0400)
committerTejun Heo <tj@kernel.org>
Fri, 18 Sep 2015 21:54:23 +0000 (17:54 -0400)
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>
include/linux/cgroup-defs.h
include/linux/cgroup.h
kernel/cgroup.c

index 10d814b..df589a0 100644 (file)
@@ -83,6 +83,17 @@ enum {
        __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
  * fundamental structural building block that controllers deal with.
@@ -123,6 +134,9 @@ struct cgroup_subsys_state {
         */
        u64 serial_nr;
 
+       /* all cgroup_files associated with this css */
+       struct list_head files;
+
        /* percpu_ref killing and RCU release */
        struct rcu_head rcu_head;
        struct work_struct destroy_work;
@@ -226,8 +240,8 @@ struct cgroup {
        int populated_cnt;
 
        struct kernfs_node *kn;         /* cgroup kernfs entry */
-       struct kernfs_node *procs_kn;   /* kn for "cgroup.procs" */
-       struct kernfs_node *events_kn;  /* kn for "cgroup.events" */
+       struct cgroup_file procs_file;  /* handle for "cgroup.procs" */
+       struct cgroup_file events_file; /* handle for "cgroup.events" */
 
        /*
         * The bitmask of subsystems enabled on the child cgroups.
@@ -335,6 +349,14 @@ struct cftype {
        /* CFTYPE_* 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
         * during registration.
index 355bf2e..fb717f2 100644 (file)
@@ -490,6 +490,19 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp)
        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 */
 
 struct cgroup_subsys_state;
index 06c9d1a..0be276f 100644 (file)
@@ -612,8 +612,8 @@ static void cgroup_update_populated(struct cgroup *cgrp, bool populated)
                if (!trigger)
                        break;
 
-               if (cgrp->events_kn)
-                       kernfs_notify(cgrp->events_kn);
+               cgroup_file_notify(&cgrp->events_file);
+
                cgrp = cgroup_parent(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.children);
+       INIT_LIST_HEAD(&cgrp->self.files);
        INIT_LIST_HEAD(&cgrp->cset_links);
        INIT_LIST_HEAD(&cgrp->pidlists);
        mutex_init(&cgrp->pidlist_mutex);
@@ -2562,7 +2563,7 @@ static int cgroup_procs_write_permission(struct task_struct *task,
                        cgrp = cgroup_parent(cgrp);
 
                ret = -ENOMEM;
-               inode = kernfs_get_inode(sb, cgrp->procs_kn);
+               inode = kernfs_get_inode(sb, cgrp->procs_file.kn);
                if (inode) {
                        ret = inode_permission(inode, MAY_WRITE);
                        iput(inode);
@@ -3253,10 +3254,14 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
                return ret;
        }
 
-       if (cft->write == cgroup_procs_write)
-               cgrp->procs_kn = kn;
-       else if (cft->seq_show == cgroup_events_show)
-               cgrp->events_kn = kn;
+       if (cft->file_offset) {
+               struct cgroup_file *cfile = (void *)css + cft->file_offset;
+
+               kernfs_get(kn);
+               cfile->kn = kn;
+               list_add(&cfile->node, &css->files);
+       }
+
        return 0;
 }
 
@@ -4408,6 +4413,7 @@ static int cgroup_clone_children_write(struct cgroup_subsys_state *css,
 static struct cftype cgroup_dfl_base_files[] = {
        {
                .name = "cgroup.procs",
+               .file_offset = offsetof(struct cgroup, procs_file),
                .seq_start = cgroup_pidlist_start,
                .seq_next = cgroup_pidlist_next,
                .seq_stop = cgroup_pidlist_stop,
@@ -4433,6 +4439,7 @@ static struct cftype cgroup_dfl_base_files[] = {
        {
                .name = "cgroup.events",
                .flags = CFTYPE_NOT_ON_ROOT,
+               .file_offset = offsetof(struct cgroup, events_file),
                .seq_show = cgroup_events_show,
        },
        { }     /* terminate */
@@ -4511,9 +4518,13 @@ static void css_free_work_fn(struct work_struct *work)
                container_of(work, struct cgroup_subsys_state, destroy_work);
        struct cgroup_subsys *ss = css->ss;
        struct cgroup *cgrp = css->cgroup;
+       struct cgroup_file *cfile;
 
        percpu_ref_exit(&css->refcnt);
 
+       list_for_each_entry(cfile, &css->files, node)
+               kernfs_put(cfile->kn);
+
        if (ss) {
                /* css free path */
                int id = css->id;
@@ -4618,6 +4629,7 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
        css->ss = ss;
        INIT_LIST_HEAD(&css->sibling);
        INIT_LIST_HEAD(&css->children);
+       INIT_LIST_HEAD(&css->files);
        css->serial_nr = css_serial_nr_next++;
 
        if (cgroup_parent(cgrp)) {