Commit 8725d5416213a145ccc9c236dbd26830ba409e00

Authored by KAMEZAWA Hiroyuki
Committed by Linus Torvalds
1 parent 116354d177

memcg: fix race in file_mapped accounting

Presently, memcg's FILE_MAPPED accounting has following race with
move_account (happens at rmdir()).

    increment page->mapcount (rmap.c)
    mem_cgroup_update_file_mapped()           move_account()
					      lock_page_cgroup()
					      check page_mapped() if
					      page_mapped(page)>1 {
						FILE_MAPPED -1 from old memcg
						FILE_MAPPED +1 to old memcg
					      }
					      .....
					      overwrite pc->mem_cgroup
					      unlock_page_cgroup()
    lock_page_cgroup()
    FILE_MAPPED + 1 to pc->mem_cgroup
    unlock_page_cgroup()

Then,
	old memcg (-1 file mapped)
	new memcg (+2 file mapped)

This happens because move_account see page_mapped() which is not guarded
by lock_page_cgroup().  This patch adds FILE_MAPPED flag to page_cgroup
and move account information based on it.  Now, all checks are synchronous
with lock_page_cgroup().

Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Balbir Singh <balbir@in.ibm.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Andrea Righi <arighi@develer.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 2 changed files with 15 additions and 9 deletions Side-by-side Diff

include/linux/page_cgroup.h
... ... @@ -39,6 +39,7 @@
39 39 PCG_CACHE, /* charged as cache */
40 40 PCG_USED, /* this object is in use. */
41 41 PCG_ACCT_LRU, /* page has been accounted for */
  42 + PCG_FILE_MAPPED, /* page is accounted as "mapped" */
42 43 };
43 44  
44 45 #define TESTPCGFLAG(uname, lname) \
... ... @@ -72,6 +73,11 @@
72 73 CLEARPCGFLAG(AcctLRU, ACCT_LRU)
73 74 TESTPCGFLAG(AcctLRU, ACCT_LRU)
74 75 TESTCLEARPCGFLAG(AcctLRU, ACCT_LRU)
  76 +
  77 +
  78 +SETPCGFLAG(FileMapped, FILE_MAPPED)
  79 +CLEARPCGFLAG(FileMapped, FILE_MAPPED)
  80 +TESTPCGFLAG(FileMapped, FILE_MAPPED)
75 81  
76 82 static inline int page_cgroup_nid(struct page_cgroup *pc)
77 83 {
... ... @@ -1359,16 +1359,19 @@
1359 1359  
1360 1360 lock_page_cgroup(pc);
1361 1361 mem = pc->mem_cgroup;
1362   - if (!mem)
  1362 + if (!mem || !PageCgroupUsed(pc))
1363 1363 goto done;
1364 1364  
1365   - if (!PageCgroupUsed(pc))
1366   - goto done;
1367   -
1368 1365 /*
1369 1366 * Preemption is already disabled. We can use __this_cpu_xxx
1370 1367 */
1371   - __this_cpu_add(mem->stat->count[MEM_CGROUP_STAT_FILE_MAPPED], val);
  1368 + if (val > 0) {
  1369 + __this_cpu_inc(mem->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);
  1370 + SetPageCgroupFileMapped(pc);
  1371 + } else {
  1372 + __this_cpu_dec(mem->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);
  1373 + ClearPageCgroupFileMapped(pc);
  1374 + }
1372 1375  
1373 1376 done:
1374 1377 unlock_page_cgroup(pc);
1375 1378  
... ... @@ -1801,16 +1804,13 @@
1801 1804 static void __mem_cgroup_move_account(struct page_cgroup *pc,
1802 1805 struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge)
1803 1806 {
1804   - struct page *page;
1805   -
1806 1807 VM_BUG_ON(from == to);
1807 1808 VM_BUG_ON(PageLRU(pc->page));
1808 1809 VM_BUG_ON(!PageCgroupLocked(pc));
1809 1810 VM_BUG_ON(!PageCgroupUsed(pc));
1810 1811 VM_BUG_ON(pc->mem_cgroup != from);
1811 1812  
1812   - page = pc->page;
1813   - if (page_mapped(page) && !PageAnon(page)) {
  1813 + if (PageCgroupFileMapped(pc)) {
1814 1814 /* Update mapped_file data for mem_cgroup */
1815 1815 preempt_disable();
1816 1816 __this_cpu_dec(from->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);