From: KAMEZAWA Hiroyuki The logic of uncharge is - decrement refcnt -> lock page cgroup -> remove page cgroup. But the logic of charging is - lock page cgroup -> increment refcnt -> return. Then, one charge will be added to a page_cgroup under being removed. This makes no big trouble (like panic) but one charge is lost. This patch add a test at charging to verify page_cgroup's refcnt is greater than 0. If not, unlock and retry. Changelog v2->v3 * adjusted to 2.6.23-mm1 Changelog v1->v2: * added cpu_relax() before retry. Signed-off-by: KAMEZAWA Hiroyuki Cc: Pavel Emelianov Cc: Paul Menage Cc: Peter Zijlstra Cc: "Eric W. Biederman" Cc: Nick Piggin Cc: Kirill Korotaev Cc: Herbert Poetzl Cc: David Rientjes Cc: Vaidyanathan Srinivasan Signed-off-by: Andrew Morton --- mm/memcontrol.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff -puN mm/memcontrol.c~bugfix-for-memory-cgroup-controller-charge-refcnt-race-fix mm/memcontrol.c --- a/mm/memcontrol.c~bugfix-for-memory-cgroup-controller-charge-refcnt-race-fix +++ a/mm/memcontrol.c @@ -281,14 +281,20 @@ int mem_cgroup_charge(struct page *page, * to see if the cgroup page already has a page_cgroup associated * with it */ +retry: lock_page_cgroup(page); pc = page_get_page_cgroup(page); /* * The page_cgroup exists and the page has already been accounted */ if (pc) { - atomic_inc(&pc->ref_cnt); - goto done; + if (unlikely(!atomic_inc_not_zero(&pc->ref_cnt))) { + /* this page is under being uncharged ? */ + unlock_page_cgroup(page); + cpu_relax(); + goto retry; + } else + goto done; } unlock_page_cgroup(page); _