mm, page_owner: track and print last migrate reason
authorVlastimil Babka <vbabka@suse.cz>
Tue, 15 Mar 2016 21:56:18 +0000 (14:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 15 Mar 2016 23:55:16 +0000 (16:55 -0700)
During migration, page_owner info is now copied with the rest of the
page, so the stacktrace leading to free page allocation during migration
is overwritten.  For debugging purposes, it might be however useful to
know that the page has been migrated since its initial allocation.  This
might happen many times during the lifetime for different reasons and
fully tracking this, especially with stacktraces would incur extra
memory costs.  As a compromise, store and print the migrate_reason of
the last migration that occurred to the page.  This is enough to
distinguish compaction, numa balancing etc.

Example page_owner entry after the patch:

  Page allocated via order 0, mask 0x24200ca(GFP_HIGHUSER_MOVABLE)
  PFN 628753 type Movable Block 1228 type Movable Flags 0x1fffff80040030(dirty|lru|swapbacked)
   [<ffffffff811682c4>] __alloc_pages_nodemask+0x134/0x230
   [<ffffffff811b6325>] alloc_pages_vma+0xb5/0x250
   [<ffffffff81177491>] shmem_alloc_page+0x61/0x90
   [<ffffffff8117a438>] shmem_getpage_gfp+0x678/0x960
   [<ffffffff8117c2b9>] shmem_fallocate+0x329/0x440
   [<ffffffff811de600>] vfs_fallocate+0x140/0x230
   [<ffffffff811df434>] SyS_fallocate+0x44/0x70
   [<ffffffff8158cc2e>] entry_SYSCALL_64_fastpath+0x12/0x71
  Page has been migrated, last migrate reason: compaction

Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/migrate.h
include/linux/page_ext.h
include/linux/page_owner.h
mm/debug.c
mm/migrate.c
mm/page_owner.c

index cac1c0904d5fb40c2a5729ca3eabd47f76a9134e..9b50325e4ddfebf4aae82c49866f617a4145ec5a 100644 (file)
@@ -23,9 +23,13 @@ enum migrate_reason {
        MR_SYSCALL,             /* also applies to cpusets */
        MR_MEMPOLICY_MBIND,
        MR_NUMA_MISPLACED,
-       MR_CMA
+       MR_CMA,
+       MR_TYPES
 };
 
+/* In mm/debug.c; also keep sync with include/trace/events/migrate.h */
+extern char *migrate_reason_names[MR_TYPES];
+
 #ifdef CONFIG_MIGRATION
 
 extern void putback_movable_pages(struct list_head *l);
index 17f118a82854960701dac4de69dfc1af43713989..e1fe7cf5bddf0935a0c9eaab5273b230c4917d1b 100644 (file)
@@ -45,6 +45,7 @@ struct page_ext {
        unsigned int order;
        gfp_t gfp_mask;
        unsigned int nr_entries;
+       int last_migrate_reason;
        unsigned long trace_entries[8];
 #endif
 };
index 6440daab4ef879f96325e526ecdf221c1315fa3a..555893bf13d703335c95133c6c9dcfb2b49bea0b 100644 (file)
@@ -12,6 +12,7 @@ extern void __set_page_owner(struct page *page,
                        unsigned int order, gfp_t gfp_mask);
 extern gfp_t __get_page_owner_gfp(struct page *page);
 extern void __copy_page_owner(struct page *oldpage, struct page *newpage);
+extern void __set_page_owner_migrate_reason(struct page *page, int reason);
 
 static inline void reset_page_owner(struct page *page, unsigned int order)
 {
@@ -38,6 +39,11 @@ static inline void copy_page_owner(struct page *oldpage, struct page *newpage)
        if (static_branch_unlikely(&page_owner_inited))
                __copy_page_owner(oldpage, newpage);
 }
+static inline void set_page_owner_migrate_reason(struct page *page, int reason)
+{
+       if (static_branch_unlikely(&page_owner_inited))
+               __set_page_owner_migrate_reason(page, reason);
+}
 #else
 static inline void reset_page_owner(struct page *page, unsigned int order)
 {
@@ -53,5 +59,8 @@ static inline gfp_t get_page_owner_gfp(struct page *page)
 static inline void copy_page_owner(struct page *oldpage, struct page *newpage)
 {
 }
+static inline void set_page_owner_migrate_reason(struct page *page, int reason)
+{
+}
 #endif /* CONFIG_PAGE_OWNER */
 #endif /* __LINUX_PAGE_OWNER_H */
index 231e1452a912a7dc7465cc92ed671b122ea388cb..78dc54877075163f577ab2dc31a1fb052bf8fe25 100644 (file)
 #include <linux/trace_events.h>
 #include <linux/memcontrol.h>
 #include <trace/events/mmflags.h>
+#include <linux/migrate.h>
 
 #include "internal.h"
 
+char *migrate_reason_names[MR_TYPES] = {
+       "compaction",
+       "memory_failure",
+       "memory_hotplug",
+       "syscall_or_cpuset",
+       "mempolicy_mbind",
+       "numa_misplaced",
+       "cma",
+};
+
 const struct trace_print_flags pageflag_names[] = {
        __def_pageflag_names,
        {0, NULL}
index 8133805431ba72a1386bdafe241968dacae022b3..432ecd0172cdf6cc825feacf367b8157cd64fe82 100644 (file)
@@ -955,8 +955,10 @@ static ICE_noinline int unmap_and_move(new_page_t get_new_page,
        }
 
        rc = __unmap_and_move(page, newpage, force, mode);
-       if (rc == MIGRATEPAGE_SUCCESS)
+       if (rc == MIGRATEPAGE_SUCCESS) {
                put_new_page = NULL;
+               set_page_owner_migrate_reason(newpage, reason);
+       }
 
 out:
        if (rc != -EAGAIN) {
@@ -1021,7 +1023,7 @@ out:
 static int unmap_and_move_huge_page(new_page_t get_new_page,
                                free_page_t put_new_page, unsigned long private,
                                struct page *hpage, int force,
-                               enum migrate_mode mode)
+                               enum migrate_mode mode, int reason)
 {
        int rc = -EAGAIN;
        int *result = NULL;
@@ -1079,6 +1081,7 @@ put_anon:
        if (rc == MIGRATEPAGE_SUCCESS) {
                hugetlb_cgroup_migrate(hpage, new_hpage);
                put_new_page = NULL;
+               set_page_owner_migrate_reason(new_hpage, reason);
        }
 
        unlock_page(hpage);
@@ -1151,7 +1154,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
                        if (PageHuge(page))
                                rc = unmap_and_move_huge_page(get_new_page,
                                                put_new_page, private, page,
-                                               pass > 2, mode);
+                                               pass > 2, mode, reason);
                        else
                                rc = unmap_and_move(get_new_page, put_new_page,
                                                private, page, pass > 2, mode,
@@ -1842,6 +1845,7 @@ fail_putback:
        set_page_memcg(new_page, page_memcg(page));
        set_page_memcg(page, NULL);
        page_remove_rmap(page, true);
+       set_page_owner_migrate_reason(new_page, MR_NUMA_MISPLACED);
 
        spin_unlock(ptl);
        mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
index 774b55623212c9e6719af228221ccf98b668f82f..a57068cfe52fdcb4758e673e7bc82df0dfb51509 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/stacktrace.h>
 #include <linux/page_owner.h>
 #include <linux/jump_label.h>
+#include <linux/migrate.h>
 #include "internal.h"
 
 static bool page_owner_disabled = true;
@@ -73,10 +74,18 @@ void __set_page_owner(struct page *page, unsigned int order, gfp_t gfp_mask)
        page_ext->order = order;
        page_ext->gfp_mask = gfp_mask;
        page_ext->nr_entries = trace.nr_entries;
+       page_ext->last_migrate_reason = -1;
 
        __set_bit(PAGE_EXT_OWNER, &page_ext->flags);
 }
 
+void __set_page_owner_migrate_reason(struct page *page, int reason)
+{
+       struct page_ext *page_ext = lookup_page_ext(page);
+
+       page_ext->last_migrate_reason = reason;
+}
+
 gfp_t __get_page_owner_gfp(struct page *page)
 {
        struct page_ext *page_ext = lookup_page_ext(page);
@@ -151,6 +160,14 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
        if (ret >= count)
                goto err;
 
+       if (page_ext->last_migrate_reason != -1) {
+               ret += snprintf(kbuf + ret, count - ret,
+                       "Page has been migrated, last migrate reason: %s\n",
+                       migrate_reason_names[page_ext->last_migrate_reason]);
+               if (ret >= count)
+                       goto err;
+       }
+
        ret += snprintf(kbuf + ret, count - ret, "\n");
        if (ret >= count)
                goto err;