mm/hotplug: treat CMA pages as unmovable
authorQian Cai <cai@lca.pw>
Fri, 19 Apr 2019 00:50:30 +0000 (17:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 19 Apr 2019 16:46:05 +0000 (09:46 -0700)
has_unmovable_pages() is used by allocating CMA and gigantic pages as
well as the memory hotplug.  The later doesn't know how to offline CMA
pool properly now, but if an unused (free) CMA page is encountered, then
has_unmovable_pages() happily considers it as a free memory and
propagates this up the call chain.  Memory offlining code then frees the
page without a proper CMA tear down which leads to an accounting issues.
Moreover if the same memory range is onlined again then the memory never
gets back to the CMA pool.

State after memory offline:

 # grep cma /proc/vmstat
 nr_free_cma 205824

 # cat /sys/kernel/debug/cma/cma-kvm_cma/count
 209920

Also, kmemleak still think those memory address are reserved below but
have already been used by the buddy allocator after onlining.  This
patch fixes the situation by treating CMA pageblocks as unmovable except
when has_unmovable_pages() is called as part of CMA allocation.

  Offlined Pages 4096
  kmemleak: Cannot insert 0xc000201f7d040008 into the object search tree (overlaps existing)
  Call Trace:
    dump_stack+0xb0/0xf4 (unreliable)
    create_object+0x344/0x380
    __kmalloc_node+0x3ec/0x860
    kvmalloc_node+0x58/0x110
    seq_read+0x41c/0x620
    __vfs_read+0x3c/0x70
    vfs_read+0xbc/0x1a0
    ksys_read+0x7c/0x140
    system_call+0x5c/0x70
  kmemleak: Kernel memory leak detector disabled
  kmemleak: Object 0xc000201cc8000000 (size 13757317120):
  kmemleak:   comm "swapper/0", pid 0, jiffies 4294937297
  kmemleak:   min_count = -1
  kmemleak:   count = 0
  kmemleak:   flags = 0x5
  kmemleak:   checksum = 0
  kmemleak:   backtrace:
       cma_declare_contiguous+0x2a4/0x3b0
       kvm_cma_reserve+0x11c/0x134
       setup_arch+0x300/0x3f8
       start_kernel+0x9c/0x6e8
       start_here_common+0x1c/0x4b0
  kmemleak: Automatic memory scanning thread ended

[cai@lca.pw: use is_migrate_cma_page() and update commit log]
Link: http://lkml.kernel.org/r/20190416170510.20048-1-cai@lca.pw
Link: http://lkml.kernel.org/r/20190413002623.8967-1-cai@lca.pw
Signed-off-by: Qian Cai <cai@lca.pw>
Acked-by: Michal Hocko <mhocko@suse.com>
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Reviewed-by: Oscar Salvador <osalvador@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/page_alloc.c

index d96ca5bc555bbc432e135c876151e0699ee88162..c6ce20aaf80bbb8e1b306eed27e53c916296eda2 100644 (file)
@@ -8005,7 +8005,10 @@ void *__init alloc_large_system_hash(const char *tablename,
 bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
                         int migratetype, int flags)
 {
-       unsigned long pfn, iter, found;
+       unsigned long found;
+       unsigned long iter = 0;
+       unsigned long pfn = page_to_pfn(page);
+       const char *reason = "unmovable page";
 
        /*
         * TODO we could make this much more efficient by not checking every
@@ -8015,17 +8018,20 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
         * can still lead to having bootmem allocations in zone_movable.
         */
 
-       /*
-        * CMA allocations (alloc_contig_range) really need to mark isolate
-        * CMA pageblocks even when they are not movable in fact so consider
-        * them movable here.
-        */
-       if (is_migrate_cma(migratetype) &&
-                       is_migrate_cma(get_pageblock_migratetype(page)))
-               return false;
+       if (is_migrate_cma_page(page)) {
+               /*
+                * CMA allocations (alloc_contig_range) really need to mark
+                * isolate CMA pageblocks even when they are not movable in fact
+                * so consider them movable here.
+                */
+               if (is_migrate_cma(migratetype))
+                       return false;
+
+               reason = "CMA page";
+               goto unmovable;
+       }
 
-       pfn = page_to_pfn(page);
-       for (found = 0, iter = 0; iter < pageblock_nr_pages; iter++) {
+       for (found = 0; iter < pageblock_nr_pages; iter++) {
                unsigned long check = pfn + iter;
 
                if (!pfn_valid_within(check))
@@ -8105,7 +8111,7 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
 unmovable:
        WARN_ON_ONCE(zone_idx(zone) == ZONE_MOVABLE);
        if (flags & REPORT_FAILURE)
-               dump_page(pfn_to_page(pfn+iter), "unmovable page");
+               dump_page(pfn_to_page(pfn + iter), reason);
        return true;
 }