summaryrefslogtreecommitdiff
path: root/mm/memcontrol.c
diff options
context:
space:
mode:
authorKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>2010-10-27 22:33:39 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-28 01:03:09 (GMT)
commit0c270f8f9988fb0d93ea214fdcff7ab90eb3d894 (patch)
treeb4f77a6714d245841ecec535ccbeba535c0d29a4 /mm/memcontrol.c
parent45531757b45cae0ce64c5aff08c2534d5a0fa3e7 (diff)
downloadlinux-fsl-qoriq-0c270f8f9988fb0d93ea214fdcff7ab90eb3d894.tar.xz
memcg: fix race in file_mapped accouting flag management
Presently memory cgroup accounts file-mapped by counter and flag. counter is working in the same way with zone_stat but FileMapped flag only exists in memcg (for helping move_account). This flag can be updated wrongly in a case. Assume CPU0 and CPU1 and a thread mapping a page on CPU0, another thread unmapping it on CPU1. CPU0 CPU1 rmv rmap (mapcount 1->0) add rmap (mapcount 0->1) lock_page_cgroup() memcg counter+1 (some delay) set MAPPED FLAG. unlock_page_cgroup() lock_page_cgroup() memcg counter-1 clear MAPPED flag In the above sequence counter is properly updated but FLAG is not. This means that representing a state by a flag which is maintained by counter needs some special care. To handle this, when clearing a flag, this patch check mapcount directly and clear the flag only when mapcount == 0. (if mapcount >0, someone will make it to zero later and flag will be cleared.) Reverse case, dec-after-inc cannot be a problem because page_table_lock() works well for it. (IOW, to make above sequence, 2 processes should touch the same page at once with map/unmap.) Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Balbir Singh <balbir@in.ibm.com> Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Cc: Greg Thelen <gthelen@google.com> 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.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 9be3cf8..0e3fdbd 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1485,7 +1485,8 @@ void mem_cgroup_update_file_mapped(struct page *page, int val)
SetPageCgroupFileMapped(pc);
} else {
__this_cpu_dec(mem->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);
- ClearPageCgroupFileMapped(pc);
+ if (!page_mapped(page)) /* for race between dec->inc counter */
+ ClearPageCgroupFileMapped(pc);
}
done: