diff options
author | Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> | 2009-01-29 17:25:11 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-29 21:04:43 -0500 |
commit | 7bcc1bb1232de6efc0b85e0c7fe38e90b2436318 (patch) | |
tree | 3952a706885f4c57217f4196f055a7c16d0af80b /mm | |
parent | baef99a08a2e23d9386b47e53fa5f0d44fc98f66 (diff) |
memcg: get/put parents at create/free
The lifetime of struct cgroup and struct mem_cgroup is different and
mem_cgroup has its own reference count for handling references from
swap_cgroup.
This causes strange problem that the parent mem_cgroup dies while child
mem_cgroup alive, and this problem causes a bug in case of
use_hierarchy==1 because res_counter_uncharge climbs up the tree.
This patch is for avoiding it by getting the parent at create, and putting
it at freeing.
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reviewed-by; KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memcontrol.c | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 4d0ea3ceba6d..76feccd26dc3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -202,6 +202,7 @@ pcg_default_flags[NR_CHARGE_TYPE] = { | |||
202 | 202 | ||
203 | static void mem_cgroup_get(struct mem_cgroup *mem); | 203 | static void mem_cgroup_get(struct mem_cgroup *mem); |
204 | static void mem_cgroup_put(struct mem_cgroup *mem); | 204 | static void mem_cgroup_put(struct mem_cgroup *mem); |
205 | static struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *mem); | ||
205 | 206 | ||
206 | static void mem_cgroup_charge_statistics(struct mem_cgroup *mem, | 207 | static void mem_cgroup_charge_statistics(struct mem_cgroup *mem, |
207 | struct page_cgroup *pc, | 208 | struct page_cgroup *pc, |
@@ -2193,10 +2194,23 @@ static void mem_cgroup_get(struct mem_cgroup *mem) | |||
2193 | 2194 | ||
2194 | static void mem_cgroup_put(struct mem_cgroup *mem) | 2195 | static void mem_cgroup_put(struct mem_cgroup *mem) |
2195 | { | 2196 | { |
2196 | if (atomic_dec_and_test(&mem->refcnt)) | 2197 | if (atomic_dec_and_test(&mem->refcnt)) { |
2198 | struct mem_cgroup *parent = parent_mem_cgroup(mem); | ||
2197 | __mem_cgroup_free(mem); | 2199 | __mem_cgroup_free(mem); |
2200 | if (parent) | ||
2201 | mem_cgroup_put(parent); | ||
2202 | } | ||
2198 | } | 2203 | } |
2199 | 2204 | ||
2205 | /* | ||
2206 | * Returns the parent mem_cgroup in memcgroup hierarchy with hierarchy enabled. | ||
2207 | */ | ||
2208 | static struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *mem) | ||
2209 | { | ||
2210 | if (!mem->res.parent) | ||
2211 | return NULL; | ||
2212 | return mem_cgroup_from_res_counter(mem->res.parent, res); | ||
2213 | } | ||
2200 | 2214 | ||
2201 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP | 2215 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP |
2202 | static void __init enable_swap_cgroup(void) | 2216 | static void __init enable_swap_cgroup(void) |
@@ -2235,6 +2249,13 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont) | |||
2235 | if (parent && parent->use_hierarchy) { | 2249 | if (parent && parent->use_hierarchy) { |
2236 | res_counter_init(&mem->res, &parent->res); | 2250 | res_counter_init(&mem->res, &parent->res); |
2237 | res_counter_init(&mem->memsw, &parent->memsw); | 2251 | res_counter_init(&mem->memsw, &parent->memsw); |
2252 | /* | ||
2253 | * We increment refcnt of the parent to ensure that we can | ||
2254 | * safely access it on res_counter_charge/uncharge. | ||
2255 | * This refcnt will be decremented when freeing this | ||
2256 | * mem_cgroup(see mem_cgroup_put). | ||
2257 | */ | ||
2258 | mem_cgroup_get(parent); | ||
2238 | } else { | 2259 | } else { |
2239 | res_counter_init(&mem->res, NULL); | 2260 | res_counter_init(&mem->res, NULL); |
2240 | res_counter_init(&mem->memsw, NULL); | 2261 | res_counter_init(&mem->memsw, NULL); |