diff options
author | Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> | 2009-01-15 16:51:12 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-15 19:39:39 -0500 |
commit | 40d58138f832a48208cdce57d6572a033b1f7a23 (patch) | |
tree | 740c3ccefa96965cb5e27f4b13dc1e03e6f688a3 /mm | |
parent | bd112db872c2f69993c86f458467acb4a14da010 (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>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memcontrol.c | 23 |
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; |
1006 | out: | 1007 | out: |
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 | |
1055 | cancel: | 1058 | cancel: |
1059 | put_page(page); | ||
1060 | uncharge: | ||
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 | ||