Commit 8725d5416213a145ccc9c236dbd26830ba409e00
Committed by
Linus Torvalds
1 parent
116354d177
Exists in
master
and in
39 other branches
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 | { |
mm/memcontrol.c
... | ... | @@ -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]); |