]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
cgroup: use a per-cgroup work for release agent
authorZefan Li <lizefan@huawei.com>
Thu, 18 Sep 2014 08:06:19 +0000 (16:06 +0800)
committerTejun Heo <tj@kernel.org>
Thu, 18 Sep 2014 17:14:22 +0000 (13:14 -0400)
Instead of using a global work to schedule release agent on removable
cgroups, we change to use a per-cgroup work to do this, which makes
the code much simpler.

v2: use a dedicated work instead of reusing css->destroy_work. (Tejun)

Signed-off-by: Zefan Li <lizefan@huawei.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
include/linux/cgroup.h
kernel/cgroup.c

index f7898e0bce1e0c09f2430f6d163eb3eebc3543eb..51958d0fb88f7ddf2febf8ac5ca05c6b75b8a572 100644 (file)
@@ -233,13 +233,6 @@ struct cgroup {
         */
        struct list_head e_csets[CGROUP_SUBSYS_COUNT];
 
-       /*
-        * Linked list running through all cgroups that can
-        * potentially be reaped by the release agent. Protected by
-        * release_list_lock
-        */
-       struct list_head release_list;
-
        /*
         * list of pidlists, up to two for each namespace (one for procs, one
         * for tasks); created on demand.
@@ -249,6 +242,9 @@ struct cgroup {
 
        /* used to wait for offlining of csses */
        wait_queue_head_t offline_waitq;
+
+       /* used to schedule release agent */
+       struct work_struct release_agent_work;
 };
 
 #define MAX_CGROUP_ROOT_NAMELEN 64
index 4ddc7558898302e37458de7db7b6bcbb71d003cd..db19a4884a7fe7a274e083ff638ea5a456b5d894 100644 (file)
@@ -392,12 +392,7 @@ static int notify_on_release(const struct cgroup *cgrp)
                        ;                                               \
                else
 
-/* the list of cgroups eligible for automatic release. Protected by
- * release_list_lock */
-static LIST_HEAD(release_list);
-static DEFINE_RAW_SPINLOCK(release_list_lock);
 static void cgroup_release_agent(struct work_struct *work);
-static DECLARE_WORK(release_agent_work, cgroup_release_agent);
 static void check_for_release(struct cgroup *cgrp);
 
 /*
@@ -1577,7 +1572,6 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
        INIT_LIST_HEAD(&cgrp->self.sibling);
        INIT_LIST_HEAD(&cgrp->self.children);
        INIT_LIST_HEAD(&cgrp->cset_links);
-       INIT_LIST_HEAD(&cgrp->release_list);
        INIT_LIST_HEAD(&cgrp->pidlists);
        mutex_init(&cgrp->pidlist_mutex);
        cgrp->self.cgroup = cgrp;
@@ -1587,6 +1581,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
                INIT_LIST_HEAD(&cgrp->e_csets[ssid]);
 
        init_waitqueue_head(&cgrp->offline_waitq);
+       INIT_WORK(&cgrp->release_agent_work, cgroup_release_agent);
 }
 
 static void init_cgroup_root(struct cgroup_root *root,
@@ -4342,6 +4337,7 @@ static void css_free_work_fn(struct work_struct *work)
                /* cgroup free path */
                atomic_dec(&cgrp->root->nr_cgrps);
                cgroup_pidlist_destroy_all(cgrp);
+               cancel_work_sync(&cgrp->release_agent_work);
 
                if (cgroup_parent(cgrp)) {
                        /*
@@ -4804,12 +4800,6 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
        for_each_css(css, ssid, cgrp)
                kill_css(css);
 
-       /* CSS_ONLINE is clear, remove from ->release_list for the last time */
-       raw_spin_lock(&release_list_lock);
-       if (!list_empty(&cgrp->release_list))
-               list_del_init(&cgrp->release_list);
-       raw_spin_unlock(&release_list_lock);
-
        /*
         * Remove @cgrp directory along with the base files.  @cgrp has an
         * extra ref on its kn.
@@ -5271,25 +5261,9 @@ void cgroup_exit(struct task_struct *tsk)
 
 static void check_for_release(struct cgroup *cgrp)
 {
-       if (cgroup_is_releasable(cgrp) && list_empty(&cgrp->cset_links) &&
-           !css_has_online_children(&cgrp->self)) {
-               /*
-                * Control Group is currently removeable. If it's not
-                * already queued for a userspace notification, queue
-                * it now
-                */
-               int need_schedule_work = 0;
-
-               raw_spin_lock(&release_list_lock);
-               if (!cgroup_is_dead(cgrp) &&
-                   list_empty(&cgrp->release_list)) {
-                       list_add(&cgrp->release_list, &release_list);
-                       need_schedule_work = 1;
-               }
-               raw_spin_unlock(&release_list_lock);
-               if (need_schedule_work)
-                       schedule_work(&release_agent_work);
-       }
+       if (cgroup_is_releasable(cgrp) && !cgroup_has_tasks(cgrp) &&
+           !css_has_online_children(&cgrp->self) && !cgroup_is_dead(cgrp))
+               schedule_work(&cgrp->release_agent_work);
 }
 
 /*
@@ -5317,52 +5291,36 @@ static void check_for_release(struct cgroup *cgrp)
  */
 static void cgroup_release_agent(struct work_struct *work)
 {
-       BUG_ON(work != &release_agent_work);
+       struct cgroup *cgrp =
+               container_of(work, struct cgroup, release_agent_work);
+       char *pathbuf = NULL, *agentbuf = NULL, *path;
+       char *argv[3], *envp[3];
+
        mutex_lock(&cgroup_mutex);
-       raw_spin_lock(&release_list_lock);
-       while (!list_empty(&release_list)) {
-               char *argv[3], *envp[3];
-               int i;
-               char *pathbuf = NULL, *agentbuf = NULL, *path;
-               struct cgroup *cgrp = list_entry(release_list.next,
-                                                   struct cgroup,
-                                                   release_list);
-               list_del_init(&cgrp->release_list);
-               raw_spin_unlock(&release_list_lock);
-               pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
-               if (!pathbuf)
-                       goto continue_free;
-               path = cgroup_path(cgrp, pathbuf, PATH_MAX);
-               if (!path)
-                       goto continue_free;
-               agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL);
-               if (!agentbuf)
-                       goto continue_free;
-
-               i = 0;
-               argv[i++] = agentbuf;
-               argv[i++] = path;
-               argv[i] = NULL;
-
-               i = 0;
-               /* minimal command environment */
-               envp[i++] = "HOME=/";
-               envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-               envp[i] = NULL;
-
-               /* Drop the lock while we invoke the usermode helper,
-                * since the exec could involve hitting disk and hence
-                * be a slow process */
-               mutex_unlock(&cgroup_mutex);
-               call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
-               mutex_lock(&cgroup_mutex);
- continue_free:
-               kfree(pathbuf);
-               kfree(agentbuf);
-               raw_spin_lock(&release_list_lock);
-       }
-       raw_spin_unlock(&release_list_lock);
+
+       pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+       agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL);
+       if (!pathbuf || !agentbuf)
+               goto out;
+
+       path = cgroup_path(cgrp, pathbuf, PATH_MAX);
+       if (!path)
+               goto out;
+
+       argv[0] = agentbuf;
+       argv[1] = path;
+       argv[2] = NULL;
+
+       /* minimal command environment */
+       envp[0] = "HOME=/";
+       envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+       envp[2] = NULL;
+
        mutex_unlock(&cgroup_mutex);
+       call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
+out:
+       kfree(agentbuf);
+       kfree(pathbuf);
 }
 
 static int __init cgroup_disable(char *str)