aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaisuke Nishimura <nishimura@mxp.nes.nec.co.jp>2009-01-15 16:51:12 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-15 19:39:39 -0500
commit40d58138f832a48208cdce57d6572a033b1f7a23 (patch)
tree740c3ccefa96965cb5e27f4b13dc1e03e6f688a3
parentbd112db872c2f69993c86f458467acb4a14da010 (diff)
memcg: fix error path of mem_cgroup_move_parent
There is a bug in error path of mem_cgroup_move_parent. Extra refcnt got from try_charge should be dropped, and usages incremented by try_charge should be decremented in both error paths: A: failure at get_page_unless_zero B: failure at isolate_lru_page This bug makes this parent directory unremovable. In case of A, rmdir doesn't return, because res.usage doesn't go down to 0 at mem_cgroup_force_empty even after all the pc in lru are removed. In case of B, rmdir fails and returns -EBUSY, because it has extra ref counts even after res.usage goes down to 0. Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Acked-by: 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>
-rw-r--r--mm/memcontrol.c23
1 files changed, 15 insertions, 8 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index b66512771167..7be9b35d7ffb 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -994,14 +994,15 @@ static int mem_cgroup_move_account(struct page_cgroup *pc,
994 if (pc->mem_cgroup != from) 994 if (pc->mem_cgroup != from)
995 goto out; 995 goto out;
996 996
997 css_put(&from->css);
998 res_counter_uncharge(&from->res, PAGE_SIZE); 997 res_counter_uncharge(&from->res, PAGE_SIZE);
999 mem_cgroup_charge_statistics(from, pc, false); 998 mem_cgroup_charge_statistics(from, pc, false);
1000 if (do_swap_account) 999 if (do_swap_account)
1001 res_counter_uncharge(&from->memsw, PAGE_SIZE); 1000 res_counter_uncharge(&from->memsw, PAGE_SIZE);
1001 css_put(&from->css);
1002
1003 css_get(&to->css);
1002 pc->mem_cgroup = to; 1004 pc->mem_cgroup = to;
1003 mem_cgroup_charge_statistics(to, pc, true); 1005 mem_cgroup_charge_statistics(to, pc, true);
1004 css_get(&to->css);
1005 ret = 0; 1006 ret = 0;
1006out: 1007out:
1007 unlock_page_cgroup(pc); 1008 unlock_page_cgroup(pc);
@@ -1034,8 +1035,10 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc,
1034 if (ret || !parent) 1035 if (ret || !parent)
1035 return ret; 1036 return ret;
1036 1037
1037 if (!get_page_unless_zero(page)) 1038 if (!get_page_unless_zero(page)) {
1038 return -EBUSY; 1039 ret = -EBUSY;
1040 goto uncharge;
1041 }
1039 1042
1040 ret = isolate_lru_page(page); 1043 ret = isolate_lru_page(page);
1041 1044
@@ -1044,19 +1047,23 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc,
1044 1047
1045 ret = mem_cgroup_move_account(pc, child, parent); 1048 ret = mem_cgroup_move_account(pc, child, parent);
1046 1049
1047 /* drop extra refcnt by try_charge() (move_account increment one) */
1048 css_put(&parent->css);
1049 putback_lru_page(page); 1050 putback_lru_page(page);
1050 if (!ret) { 1051 if (!ret) {
1051 put_page(page); 1052 put_page(page);
1053 /* drop extra refcnt by try_charge() */
1054 css_put(&parent->css);
1052 return 0; 1055 return 0;
1053 } 1056 }
1054 /* uncharge if move fails */ 1057
1055cancel: 1058cancel:
1059 put_page(page);
1060uncharge:
1061 /* drop extra refcnt by try_charge() */
1062 css_put(&parent->css);
1063 /* uncharge if move fails */
1056 res_counter_uncharge(&parent->res, PAGE_SIZE); 1064 res_counter_uncharge(&parent->res, PAGE_SIZE);
1057 if (do_swap_account) 1065 if (do_swap_account)
1058 res_counter_uncharge(&parent->memsw, PAGE_SIZE); 1066 res_counter_uncharge(&parent->memsw, PAGE_SIZE);
1059 put_page(page);
1060 return ret; 1067 return ret;
1061} 1068}
1062 1069