aboutsummaryrefslogtreecommitdiffstats
path: root/mm/memcontrol.c
diff options
context:
space:
mode:
authorJohannes Weiner <jweiner@redhat.com>2011-11-02 16:38:29 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-11-02 19:07:00 -0400
commita61ed3cec51cfd4877855c24890ab8d3e2b143e3 (patch)
tree3ffb850513b202c8eb0ac09ad6d307719bd39856 /mm/memcontrol.c
parent9b272977e3b99a8699361d214b51f98c8a9e0e7b (diff)
memcg: close race between charge and putback
There is a potential race between a thread charging a page and another thread putting it back to the LRU list: charge: putback: SetPageCgroupUsed SetPageLRU PageLRU && add to memcg LRU PageCgroupUsed && add to memcg LRU The order of setting one flag and checking the other is crucial, otherwise the charge may observe !PageLRU while the putback observes !PageCgroupUsed and the page is not linked to the memcg LRU at all. Global memory pressure may fix this by trying to isolate and putback the page for reclaim, where that putback would link it to the memcg LRU again. Without that, the memory cgroup is undeletable due to a charge whose physical page can not be found and moved out. Signed-off-by: Johannes Weiner <jweiner@redhat.com> Cc: Ying Han <yinghan@google.com> Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Cc: Balbir Singh <bsingharora@gmail.com> Cc: Michal Hocko <mhocko@suse.cz> 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.c21
1 files changed, 20 insertions, 1 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index ce7b35d024e9..01e0c725de65 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -993,6 +993,16 @@ void mem_cgroup_add_lru_list(struct page *page, enum lru_list lru)
993 return; 993 return;
994 pc = lookup_page_cgroup(page); 994 pc = lookup_page_cgroup(page);
995 VM_BUG_ON(PageCgroupAcctLRU(pc)); 995 VM_BUG_ON(PageCgroupAcctLRU(pc));
996 /*
997 * putback: charge:
998 * SetPageLRU SetPageCgroupUsed
999 * smp_mb smp_mb
1000 * PageCgroupUsed && add to memcg LRU PageLRU && add to memcg LRU
1001 *
1002 * Ensure that one of the two sides adds the page to the memcg
1003 * LRU during a race.
1004 */
1005 smp_mb();
996 if (!PageCgroupUsed(pc)) 1006 if (!PageCgroupUsed(pc))
997 return; 1007 return;
998 /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ 1008 /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */
@@ -1044,7 +1054,16 @@ static void mem_cgroup_lru_add_after_commit(struct page *page)
1044 unsigned long flags; 1054 unsigned long flags;
1045 struct zone *zone = page_zone(page); 1055 struct zone *zone = page_zone(page);
1046 struct page_cgroup *pc = lookup_page_cgroup(page); 1056 struct page_cgroup *pc = lookup_page_cgroup(page);
1047 1057 /*
1058 * putback: charge:
1059 * SetPageLRU SetPageCgroupUsed
1060 * smp_mb smp_mb
1061 * PageCgroupUsed && add to memcg LRU PageLRU && add to memcg LRU
1062 *
1063 * Ensure that one of the two sides adds the page to the memcg
1064 * LRU during a race.
1065 */
1066 smp_mb();
1048 /* taking care of that the page is added to LRU while we commit it */ 1067 /* taking care of that the page is added to LRU while we commit it */
1049 if (likely(!PageLRU(page))) 1068 if (likely(!PageLRU(page)))
1050 return; 1069 return;