mm: more checks on free_pages_prepare() for tail pages
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>
Wed, 11 Feb 2015 23:25:52 +0000 (15:25 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 12 Feb 2015 01:06:02 +0000 (17:06 -0800)
Although it was not called, destroy_compound_page() did some potentially
useful checks.  Let's re-introduce them in free_pages_prepare(), where
they can be actually triggered when CONFIG_DEBUG_VM=y.

compound_order() assert is already in free_pages_prepare().  We have few
checks for tail pages left.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/page_alloc.c

index 12d55b8..0081228 100644 (file)
@@ -764,21 +764,40 @@ static void free_one_page(struct zone *zone,
        spin_unlock(&zone->lock);
 }
 
+static int free_tail_pages_check(struct page *head_page, struct page *page)
+{
+       if (!IS_ENABLED(CONFIG_DEBUG_VM))
+               return 0;
+       if (unlikely(!PageTail(page))) {
+               bad_page(page, "PageTail not set", 0);
+               return 1;
+       }
+       if (unlikely(page->first_page != head_page)) {
+               bad_page(page, "first_page not consistent", 0);
+               return 1;
+       }
+       return 0;
+}
+
 static bool free_pages_prepare(struct page *page, unsigned int order)
 {
-       int i;
-       int bad = 0;
+       bool compound = PageCompound(page);
+       int i, bad = 0;
 
        VM_BUG_ON_PAGE(PageTail(page), page);
-       VM_BUG_ON_PAGE(PageHead(page) && compound_order(page) != order, page);
+       VM_BUG_ON_PAGE(compound && compound_order(page) != order, page);
 
        trace_mm_page_free(page, order);
        kmemcheck_free_shadow(page, order);
 
        if (PageAnon(page))
                page->mapping = NULL;
-       for (i = 0; i < (1 << order); i++)
+       bad += free_pages_check(page);
+       for (i = 1; i < (1 << order); i++) {
+               if (compound)
+                       bad += free_tail_pages_check(page, page + i);
                bad += free_pages_check(page + i);
+       }
        if (bad)
                return false;