]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mm: memcg: remove incorrect VM_BUG_ON for swap cache pages in uncharge
authorJohannes Weiner <hannes@cmpxchg.org>
Thu, 9 May 2013 23:57:00 +0000 (09:57 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Tue, 21 May 2013 04:17:33 +0000 (14:17 +1000)
0c59b89 ("mm: memcg: push down PageSwapCache check into uncharge entry
functions") added a VM_BUG_ON() on PageSwapCache in the uncharge path
after checking that page flag once, assuming that the state is stable in
all paths, but this is not the case and the condition triggers in user
environments.  An uncharge after the last page table reference to the page
goes away can race with reclaim adding the page to swap cache.

Swap cache pages are usually uncharged when they are freed after swapout,
from a path that also handles swap usage accounting and memcg lifetime
management.  However, since the last page table reference is gone and thus
no references to the swap slot left, the swap slot will be freed shortly
when reclaim attempts to write the page to disk.  The whole swap
accounting is not even necessary.

So while the race condition for which this VM_BUG_ON was added is real and
actually existed all along, there are no negative effects.  Remove the
VM_BUG_ON again.

Reported-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Reported-by: Lingzhu Xiang <lxiang@redhat.com>
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Hugh Dickins <hughd@google.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/memcontrol.c

index cb1c9dedf9b65c08a4a6d9d6f81ee01f2cb44c36..010d6c14129ae320ab9655a573d49af33e034659 100644 (file)
@@ -4108,8 +4108,6 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype,
        if (mem_cgroup_disabled())
                return NULL;
 
-       VM_BUG_ON(PageSwapCache(page));
-
        if (PageTransHuge(page)) {
                nr_pages <<= compound_order(page);
                VM_BUG_ON(!PageTransHuge(page));
@@ -4205,6 +4203,18 @@ void mem_cgroup_uncharge_page(struct page *page)
        if (page_mapped(page))
                return;
        VM_BUG_ON(page->mapping && !PageAnon(page));
+       /*
+        * If the page is in swap cache, uncharge should be deferred
+        * to the swap path, which also properly accounts swap usage
+        * and handles memcg lifetime.
+        *
+        * Note that this check is not stable and reclaim may add the
+        * page to swap cache at any time after this.  However, if the
+        * page is not in swap cache by the time page->mapcount hits
+        * 0, there won't be any page table references to the swap
+        * slot, and reclaim will free it and not actually write the
+        * page to disk.
+        */
        if (PageSwapCache(page))
                return;
        __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON, false);