diff options
author | Mandeep Singh Baines <msb@chromium.org> | 2012-01-30 15:51:56 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2012-01-30 16:01:39 -0500 |
commit | 61d1d219c4c0761059236a46867bc49943c4d29d (patch) | |
tree | 3803019bf5a294644bc3effcd95dac928ead587d /kernel/cgroup.c | |
parent | fb5d2b4cfc24963d0e8a7df57de1ecffa10a04cf (diff) |
cgroup: remove extra calls to find_existing_css_set
In cgroup_attach_proc, we indirectly call find_existing_css_set 3
times. It is an expensive call so we want to call it a minimum
of times. This patch only calls it once and stores the result so
that it can be used later on when we call cgroup_task_migrate.
This required modifying cgroup_task_migrate to take the new css_set
(which we obtained from find_css_set) as a parameter. The nice side
effect of this is that cgroup_task_migrate is now identical for
cgroup_attach_task and cgroup_attach_proc. It also now returns a
void since it can never fail.
Changes in V5:
* https://lkml.org/lkml/2012/1/20/344 (Tejun Heo)
* Remove css_set_refs
Changes in V4:
* https://lkml.org/lkml/2011/12/22/421 (Li Zefan)
* Avoid GFP_KERNEL (sleep) in rcu_read_lock by getting css_set in
a separate loop not under an rcu_read_lock
Changes in V3:
* https://lkml.org/lkml/2011/12/22/13 (Li Zefan)
* Fixed earlier bug by creating a seperate patch to remove tasklist_lock
Changes in V2:
* https://lkml.org/lkml/2011/12/20/372 (Tejun Heo)
* Move find_css_set call into loop which creates the flex array
* Author
* Kill css_set_refs and use group_size instead
* Fix an off-by-one error in counting css_set refs
* Add a retval check in out_list_teardown
Signed-off-by: Mandeep Singh Baines <msb@chromium.org>
Acked-by: Li Zefan <lizf@cn.fujitsu.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: containers@lists.linux-foundation.org
Cc: cgroups@vger.kernel.org
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Paul Menage <paul@paulmenage.org>
Diffstat (limited to 'kernel/cgroup.c')
-rw-r--r-- | kernel/cgroup.c | 140 |
1 files changed, 27 insertions, 113 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 1626152dcc1e..43a224f167b5 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -1763,6 +1763,7 @@ EXPORT_SYMBOL_GPL(cgroup_path); | |||
1763 | struct task_and_cgroup { | 1763 | struct task_and_cgroup { |
1764 | struct task_struct *task; | 1764 | struct task_struct *task; |
1765 | struct cgroup *cgrp; | 1765 | struct cgroup *cgrp; |
1766 | struct css_set *cg; | ||
1766 | }; | 1767 | }; |
1767 | 1768 | ||
1768 | struct cgroup_taskset { | 1769 | struct cgroup_taskset { |
@@ -1843,11 +1844,10 @@ EXPORT_SYMBOL_GPL(cgroup_taskset_size); | |||
1843 | * will already exist. If not set, this function might sleep, and can fail with | 1844 | * will already exist. If not set, this function might sleep, and can fail with |
1844 | * -ENOMEM. Must be called with cgroup_mutex and threadgroup locked. | 1845 | * -ENOMEM. Must be called with cgroup_mutex and threadgroup locked. |
1845 | */ | 1846 | */ |
1846 | static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp, | 1847 | static void cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp, |
1847 | struct task_struct *tsk, bool guarantee) | 1848 | struct task_struct *tsk, struct css_set *newcg) |
1848 | { | 1849 | { |
1849 | struct css_set *oldcg; | 1850 | struct css_set *oldcg; |
1850 | struct css_set *newcg; | ||
1851 | 1851 | ||
1852 | /* | 1852 | /* |
1853 | * We are synchronized through threadgroup_lock() against PF_EXITING | 1853 | * We are synchronized through threadgroup_lock() against PF_EXITING |
@@ -1857,23 +1857,6 @@ static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp, | |||
1857 | WARN_ON_ONCE(tsk->flags & PF_EXITING); | 1857 | WARN_ON_ONCE(tsk->flags & PF_EXITING); |
1858 | oldcg = tsk->cgroups; | 1858 | oldcg = tsk->cgroups; |
1859 | 1859 | ||
1860 | /* locate or allocate a new css_set for this task. */ | ||
1861 | if (guarantee) { | ||
1862 | /* we know the css_set we want already exists. */ | ||
1863 | struct cgroup_subsys_state *template[CGROUP_SUBSYS_COUNT]; | ||
1864 | read_lock(&css_set_lock); | ||
1865 | newcg = find_existing_css_set(oldcg, cgrp, template); | ||
1866 | BUG_ON(!newcg); | ||
1867 | get_css_set(newcg); | ||
1868 | read_unlock(&css_set_lock); | ||
1869 | } else { | ||
1870 | might_sleep(); | ||
1871 | /* find_css_set will give us newcg already referenced. */ | ||
1872 | newcg = find_css_set(oldcg, cgrp); | ||
1873 | if (!newcg) | ||
1874 | return -ENOMEM; | ||
1875 | } | ||
1876 | |||
1877 | task_lock(tsk); | 1860 | task_lock(tsk); |
1878 | rcu_assign_pointer(tsk->cgroups, newcg); | 1861 | rcu_assign_pointer(tsk->cgroups, newcg); |
1879 | task_unlock(tsk); | 1862 | task_unlock(tsk); |
@@ -1892,7 +1875,6 @@ static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp, | |||
1892 | put_css_set(oldcg); | 1875 | put_css_set(oldcg); |
1893 | 1876 | ||
1894 | set_bit(CGRP_RELEASABLE, &oldcgrp->flags); | 1877 | set_bit(CGRP_RELEASABLE, &oldcgrp->flags); |
1895 | return 0; | ||
1896 | } | 1878 | } |
1897 | 1879 | ||
1898 | /** | 1880 | /** |
@@ -1910,6 +1892,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) | |||
1910 | struct cgroup *oldcgrp; | 1892 | struct cgroup *oldcgrp; |
1911 | struct cgroupfs_root *root = cgrp->root; | 1893 | struct cgroupfs_root *root = cgrp->root; |
1912 | struct cgroup_taskset tset = { }; | 1894 | struct cgroup_taskset tset = { }; |
1895 | struct css_set *newcg; | ||
1913 | 1896 | ||
1914 | /* @tsk either already exited or can't exit until the end */ | 1897 | /* @tsk either already exited or can't exit until the end */ |
1915 | if (tsk->flags & PF_EXITING) | 1898 | if (tsk->flags & PF_EXITING) |
@@ -1939,9 +1922,13 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) | |||
1939 | } | 1922 | } |
1940 | } | 1923 | } |
1941 | 1924 | ||
1942 | retval = cgroup_task_migrate(cgrp, oldcgrp, tsk, false); | 1925 | newcg = find_css_set(tsk->cgroups, cgrp); |
1943 | if (retval) | 1926 | if (!newcg) { |
1927 | retval = -ENOMEM; | ||
1944 | goto out; | 1928 | goto out; |
1929 | } | ||
1930 | |||
1931 | cgroup_task_migrate(cgrp, oldcgrp, tsk, newcg); | ||
1945 | 1932 | ||
1946 | for_each_subsys(root, ss) { | 1933 | for_each_subsys(root, ss) { |
1947 | if (ss->attach) | 1934 | if (ss->attach) |
@@ -1997,66 +1984,6 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) | |||
1997 | } | 1984 | } |
1998 | EXPORT_SYMBOL_GPL(cgroup_attach_task_all); | 1985 | EXPORT_SYMBOL_GPL(cgroup_attach_task_all); |
1999 | 1986 | ||
2000 | /* | ||
2001 | * cgroup_attach_proc works in two stages, the first of which prefetches all | ||
2002 | * new css_sets needed (to make sure we have enough memory before committing | ||
2003 | * to the move) and stores them in a list of entries of the following type. | ||
2004 | * TODO: possible optimization: use css_set->rcu_head for chaining instead | ||
2005 | */ | ||
2006 | struct cg_list_entry { | ||
2007 | struct css_set *cg; | ||
2008 | struct list_head links; | ||
2009 | }; | ||
2010 | |||
2011 | static bool css_set_check_fetched(struct cgroup *cgrp, | ||
2012 | struct task_struct *tsk, struct css_set *cg, | ||
2013 | struct list_head *newcg_list) | ||
2014 | { | ||
2015 | struct css_set *newcg; | ||
2016 | struct cg_list_entry *cg_entry; | ||
2017 | struct cgroup_subsys_state *template[CGROUP_SUBSYS_COUNT]; | ||
2018 | |||
2019 | read_lock(&css_set_lock); | ||
2020 | newcg = find_existing_css_set(cg, cgrp, template); | ||
2021 | read_unlock(&css_set_lock); | ||
2022 | |||
2023 | /* doesn't exist at all? */ | ||
2024 | if (!newcg) | ||
2025 | return false; | ||
2026 | /* see if it's already in the list */ | ||
2027 | list_for_each_entry(cg_entry, newcg_list, links) | ||
2028 | if (cg_entry->cg == newcg) | ||
2029 | return true; | ||
2030 | |||
2031 | /* not found */ | ||
2032 | return false; | ||
2033 | } | ||
2034 | |||
2035 | /* | ||
2036 | * Find the new css_set and store it in the list in preparation for moving the | ||
2037 | * given task to the given cgroup. Returns 0 or -ENOMEM. | ||
2038 | */ | ||
2039 | static int css_set_prefetch(struct cgroup *cgrp, struct css_set *cg, | ||
2040 | struct list_head *newcg_list) | ||
2041 | { | ||
2042 | struct css_set *newcg; | ||
2043 | struct cg_list_entry *cg_entry; | ||
2044 | |||
2045 | /* ensure a new css_set will exist for this thread */ | ||
2046 | newcg = find_css_set(cg, cgrp); | ||
2047 | if (!newcg) | ||
2048 | return -ENOMEM; | ||
2049 | /* add it to the list */ | ||
2050 | cg_entry = kmalloc(sizeof(struct cg_list_entry), GFP_KERNEL); | ||
2051 | if (!cg_entry) { | ||
2052 | put_css_set(newcg); | ||
2053 | return -ENOMEM; | ||
2054 | } | ||
2055 | cg_entry->cg = newcg; | ||
2056 | list_add(&cg_entry->links, newcg_list); | ||
2057 | return 0; | ||
2058 | } | ||
2059 | |||
2060 | /** | 1987 | /** |
2061 | * cgroup_attach_proc - attach all threads in a threadgroup to a cgroup | 1988 | * cgroup_attach_proc - attach all threads in a threadgroup to a cgroup |
2062 | * @cgrp: the cgroup to attach to | 1989 | * @cgrp: the cgroup to attach to |
@@ -2070,20 +1997,12 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) | |||
2070 | int retval, i, group_size; | 1997 | int retval, i, group_size; |
2071 | struct cgroup_subsys *ss, *failed_ss = NULL; | 1998 | struct cgroup_subsys *ss, *failed_ss = NULL; |
2072 | /* guaranteed to be initialized later, but the compiler needs this */ | 1999 | /* guaranteed to be initialized later, but the compiler needs this */ |
2073 | struct css_set *oldcg; | ||
2074 | struct cgroupfs_root *root = cgrp->root; | 2000 | struct cgroupfs_root *root = cgrp->root; |
2075 | /* threadgroup list cursor and array */ | 2001 | /* threadgroup list cursor and array */ |
2076 | struct task_struct *tsk; | 2002 | struct task_struct *tsk; |
2077 | struct task_and_cgroup *tc; | 2003 | struct task_and_cgroup *tc; |
2078 | struct flex_array *group; | 2004 | struct flex_array *group; |
2079 | struct cgroup_taskset tset = { }; | 2005 | struct cgroup_taskset tset = { }; |
2080 | /* | ||
2081 | * we need to make sure we have css_sets for all the tasks we're | ||
2082 | * going to move -before- we actually start moving them, so that in | ||
2083 | * case we get an ENOMEM we can bail out before making any changes. | ||
2084 | */ | ||
2085 | struct list_head newcg_list; | ||
2086 | struct cg_list_entry *cg_entry, *temp_nobe; | ||
2087 | 2006 | ||
2088 | /* | 2007 | /* |
2089 | * step 0: in order to do expensive, possibly blocking operations for | 2008 | * step 0: in order to do expensive, possibly blocking operations for |
@@ -2119,15 +2038,15 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) | |||
2119 | 2038 | ||
2120 | /* as per above, nr_threads may decrease, but not increase. */ | 2039 | /* as per above, nr_threads may decrease, but not increase. */ |
2121 | BUG_ON(i >= group_size); | 2040 | BUG_ON(i >= group_size); |
2122 | /* | ||
2123 | * saying GFP_ATOMIC has no effect here because we did prealloc | ||
2124 | * earlier, but it's good form to communicate our expectations. | ||
2125 | */ | ||
2126 | ent.task = tsk; | 2041 | ent.task = tsk; |
2127 | ent.cgrp = task_cgroup_from_root(tsk, root); | 2042 | ent.cgrp = task_cgroup_from_root(tsk, root); |
2128 | /* nothing to do if this task is already in the cgroup */ | 2043 | /* nothing to do if this task is already in the cgroup */ |
2129 | if (ent.cgrp == cgrp) | 2044 | if (ent.cgrp == cgrp) |
2130 | continue; | 2045 | continue; |
2046 | /* | ||
2047 | * saying GFP_ATOMIC has no effect here because we did prealloc | ||
2048 | * earlier, but it's good form to communicate our expectations. | ||
2049 | */ | ||
2131 | retval = flex_array_put(group, i, &ent, GFP_ATOMIC); | 2050 | retval = flex_array_put(group, i, &ent, GFP_ATOMIC); |
2132 | BUG_ON(retval != 0); | 2051 | BUG_ON(retval != 0); |
2133 | i++; | 2052 | i++; |
@@ -2160,17 +2079,12 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) | |||
2160 | * step 2: make sure css_sets exist for all threads to be migrated. | 2079 | * step 2: make sure css_sets exist for all threads to be migrated. |
2161 | * we use find_css_set, which allocates a new one if necessary. | 2080 | * we use find_css_set, which allocates a new one if necessary. |
2162 | */ | 2081 | */ |
2163 | INIT_LIST_HEAD(&newcg_list); | ||
2164 | for (i = 0; i < group_size; i++) { | 2082 | for (i = 0; i < group_size; i++) { |
2165 | tc = flex_array_get(group, i); | 2083 | tc = flex_array_get(group, i); |
2166 | oldcg = tc->task->cgroups; | 2084 | tc->cg = find_css_set(tc->task->cgroups, cgrp); |
2167 | 2085 | if (!tc->cg) { | |
2168 | /* if we don't already have it in the list get a new one */ | 2086 | retval = -ENOMEM; |
2169 | if (!css_set_check_fetched(cgrp, tc->task, oldcg, | 2087 | goto out_put_css_set_refs; |
2170 | &newcg_list)) { | ||
2171 | retval = css_set_prefetch(cgrp, oldcg, &newcg_list); | ||
2172 | if (retval) | ||
2173 | goto out_list_teardown; | ||
2174 | } | 2088 | } |
2175 | } | 2089 | } |
2176 | 2090 | ||
@@ -2181,8 +2095,7 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) | |||
2181 | */ | 2095 | */ |
2182 | for (i = 0; i < group_size; i++) { | 2096 | for (i = 0; i < group_size; i++) { |
2183 | tc = flex_array_get(group, i); | 2097 | tc = flex_array_get(group, i); |
2184 | retval = cgroup_task_migrate(cgrp, tc->cgrp, tc->task, true); | 2098 | cgroup_task_migrate(cgrp, tc->cgrp, tc->task, tc->cg); |
2185 | BUG_ON(retval); | ||
2186 | } | 2099 | } |
2187 | /* nothing is sensitive to fork() after this point. */ | 2100 | /* nothing is sensitive to fork() after this point. */ |
2188 | 2101 | ||
@@ -2200,15 +2113,16 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) | |||
2200 | synchronize_rcu(); | 2113 | synchronize_rcu(); |
2201 | cgroup_wakeup_rmdir_waiter(cgrp); | 2114 | cgroup_wakeup_rmdir_waiter(cgrp); |
2202 | retval = 0; | 2115 | retval = 0; |
2203 | out_list_teardown: | 2116 | out_put_css_set_refs: |
2204 | /* clean up the list of prefetched css_sets. */ | 2117 | if (retval) { |
2205 | list_for_each_entry_safe(cg_entry, temp_nobe, &newcg_list, links) { | 2118 | for (i = 0; i < group_size; i++) { |
2206 | list_del(&cg_entry->links); | 2119 | tc = flex_array_get(group, i); |
2207 | put_css_set(cg_entry->cg); | 2120 | if (!tc->cg) |
2208 | kfree(cg_entry); | 2121 | break; |
2122 | put_css_set(tc->cg); | ||
2123 | } | ||
2209 | } | 2124 | } |
2210 | out_cancel_attach: | 2125 | out_cancel_attach: |
2211 | /* same deal as in cgroup_attach_task */ | ||
2212 | if (retval) { | 2126 | if (retval) { |
2213 | for_each_subsys(root, ss) { | 2127 | for_each_subsys(root, ss) { |
2214 | if (ss == failed_ss) | 2128 | if (ss == failed_ss) |