aboutsummaryrefslogtreecommitdiffstats
path: root/mm/memcontrol.c
diff options
context:
space:
mode:
authorKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>2011-01-20 17:44:25 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-20 20:02:06 -0500
commit987eba66e0e6aa654d60881a14731a353ee0acb4 (patch)
tree04138f68481ccd3e8f9447037c3737c568fa3330 /mm/memcontrol.c
parentece35ca810326946ddc930c43356312ad5de44d4 (diff)
memcg: fix rmdir, force_empty with THP
Now, when THP is enabled, memcg's rmdir() function is broken because move_account() for THP page is not supported. This will cause account leak or -EBUSY issue at rmdir(). This patch fixes the issue by supporting move_account() THP pages. Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Cc: Balbir Singh <balbir@linux.vnet.ibm.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r--mm/memcontrol.c37
1 files changed, 26 insertions, 11 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 7a94ef6b35e2..5b562b375cbd 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2197,8 +2197,11 @@ void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail)
2197 */ 2197 */
2198 2198
2199static void __mem_cgroup_move_account(struct page_cgroup *pc, 2199static void __mem_cgroup_move_account(struct page_cgroup *pc,
2200 struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge) 2200 struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge,
2201 int charge_size)
2201{ 2202{
2203 int nr_pages = charge_size >> PAGE_SHIFT;
2204
2202 VM_BUG_ON(from == to); 2205 VM_BUG_ON(from == to);
2203 VM_BUG_ON(PageLRU(pc->page)); 2206 VM_BUG_ON(PageLRU(pc->page));
2204 VM_BUG_ON(!page_is_cgroup_locked(pc)); 2207 VM_BUG_ON(!page_is_cgroup_locked(pc));
@@ -2212,14 +2215,14 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc,
2212 __this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]); 2215 __this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);
2213 preempt_enable(); 2216 preempt_enable();
2214 } 2217 }
2215 mem_cgroup_charge_statistics(from, PageCgroupCache(pc), -1); 2218 mem_cgroup_charge_statistics(from, PageCgroupCache(pc), -nr_pages);
2216 if (uncharge) 2219 if (uncharge)
2217 /* This is not "cancel", but cancel_charge does all we need. */ 2220 /* This is not "cancel", but cancel_charge does all we need. */
2218 mem_cgroup_cancel_charge(from, PAGE_SIZE); 2221 mem_cgroup_cancel_charge(from, charge_size);
2219 2222
2220 /* caller should have done css_get */ 2223 /* caller should have done css_get */
2221 pc->mem_cgroup = to; 2224 pc->mem_cgroup = to;
2222 mem_cgroup_charge_statistics(to, PageCgroupCache(pc), 1); 2225 mem_cgroup_charge_statistics(to, PageCgroupCache(pc), nr_pages);
2223 /* 2226 /*
2224 * We charges against "to" which may not have any tasks. Then, "to" 2227 * We charges against "to" which may not have any tasks. Then, "to"
2225 * can be under rmdir(). But in current implementation, caller of 2228 * can be under rmdir(). But in current implementation, caller of
@@ -2234,15 +2237,19 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc,
2234 * __mem_cgroup_move_account() 2237 * __mem_cgroup_move_account()
2235 */ 2238 */
2236static int mem_cgroup_move_account(struct page_cgroup *pc, 2239static int mem_cgroup_move_account(struct page_cgroup *pc,
2237 struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge) 2240 struct mem_cgroup *from, struct mem_cgroup *to,
2241 bool uncharge, int charge_size)
2238{ 2242{
2239 int ret = -EINVAL; 2243 int ret = -EINVAL;
2240 unsigned long flags; 2244 unsigned long flags;
2241 2245
2246 if ((charge_size > PAGE_SIZE) && !PageTransHuge(pc->page))
2247 return -EBUSY;
2248
2242 lock_page_cgroup(pc); 2249 lock_page_cgroup(pc);
2243 if (PageCgroupUsed(pc) && pc->mem_cgroup == from) { 2250 if (PageCgroupUsed(pc) && pc->mem_cgroup == from) {
2244 move_lock_page_cgroup(pc, &flags); 2251 move_lock_page_cgroup(pc, &flags);
2245 __mem_cgroup_move_account(pc, from, to, uncharge); 2252 __mem_cgroup_move_account(pc, from, to, uncharge, charge_size);
2246 move_unlock_page_cgroup(pc, &flags); 2253 move_unlock_page_cgroup(pc, &flags);
2247 ret = 0; 2254 ret = 0;
2248 } 2255 }
@@ -2267,6 +2274,8 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc,
2267 struct cgroup *cg = child->css.cgroup; 2274 struct cgroup *cg = child->css.cgroup;
2268 struct cgroup *pcg = cg->parent; 2275 struct cgroup *pcg = cg->parent;
2269 struct mem_cgroup *parent; 2276 struct mem_cgroup *parent;
2277 int charge = PAGE_SIZE;
2278 unsigned long flags;
2270 int ret; 2279 int ret;
2271 2280
2272 /* Is ROOT ? */ 2281 /* Is ROOT ? */
@@ -2278,17 +2287,23 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc,
2278 goto out; 2287 goto out;
2279 if (isolate_lru_page(page)) 2288 if (isolate_lru_page(page))
2280 goto put; 2289 goto put;
2290 /* The page is isolated from LRU and we have no race with splitting */
2291 charge = PAGE_SIZE << compound_order(page);
2281 2292
2282 parent = mem_cgroup_from_cont(pcg); 2293 parent = mem_cgroup_from_cont(pcg);
2283 ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, 2294 ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, charge);
2284 PAGE_SIZE);
2285 if (ret || !parent) 2295 if (ret || !parent)
2286 goto put_back; 2296 goto put_back;
2287 2297
2288 ret = mem_cgroup_move_account(pc, child, parent, true); 2298 if (charge > PAGE_SIZE)
2299 flags = compound_lock_irqsave(page);
2300
2301 ret = mem_cgroup_move_account(pc, child, parent, true, charge);
2289 if (ret) 2302 if (ret)
2290 mem_cgroup_cancel_charge(parent, PAGE_SIZE); 2303 mem_cgroup_cancel_charge(parent, charge);
2291put_back: 2304put_back:
2305 if (charge > PAGE_SIZE)
2306 compound_unlock_irqrestore(page, flags);
2292 putback_lru_page(page); 2307 putback_lru_page(page);
2293put: 2308put:
2294 put_page(page); 2309 put_page(page);
@@ -4868,7 +4883,7 @@ retry:
4868 goto put; 4883 goto put;
4869 pc = lookup_page_cgroup(page); 4884 pc = lookup_page_cgroup(page);
4870 if (!mem_cgroup_move_account(pc, 4885 if (!mem_cgroup_move_account(pc,
4871 mc.from, mc.to, false)) { 4886 mc.from, mc.to, false, PAGE_SIZE)) {
4872 mc.precharge--; 4887 mc.precharge--;
4873 /* we uncharge from mc.from later. */ 4888 /* we uncharge from mc.from later. */
4874 mc.moved_charge++; 4889 mc.moved_charge++;