libceph: introduce and switch to reopen_session()
[linux-2.6-block.git] / mm / page_owner.c
index 983c3a10fa07058df249c64d5c89b9578661f672..ac3d8d129974398cb98bf0bbf2b56207fdc18642 100644 (file)
@@ -5,10 +5,12 @@
 #include <linux/bootmem.h>
 #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;
-bool page_owner_inited __read_mostly;
+DEFINE_STATIC_KEY_FALSE(page_owner_inited);
 
 static void init_early_allocated_pages(void);
 
@@ -37,7 +39,7 @@ static void init_page_owner(void)
        if (page_owner_disabled)
                return;
 
-       page_owner_inited = true;
+       static_branch_enable(&page_owner_inited);
        init_early_allocated_pages();
 }
 
@@ -72,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);
@@ -83,6 +93,31 @@ gfp_t __get_page_owner_gfp(struct page *page)
        return page_ext->gfp_mask;
 }
 
+void __copy_page_owner(struct page *oldpage, struct page *newpage)
+{
+       struct page_ext *old_ext = lookup_page_ext(oldpage);
+       struct page_ext *new_ext = lookup_page_ext(newpage);
+       int i;
+
+       new_ext->order = old_ext->order;
+       new_ext->gfp_mask = old_ext->gfp_mask;
+       new_ext->nr_entries = old_ext->nr_entries;
+
+       for (i = 0; i < ARRAY_SIZE(new_ext->trace_entries); i++)
+               new_ext->trace_entries[i] = old_ext->trace_entries[i];
+
+       /*
+        * We don't clear the bit on the oldpage as it's going to be freed
+        * after migration. Until then, the info can be useful in case of
+        * a bug, and the overal stats will be off a bit only temporarily.
+        * Also, migrate_misplaced_transhuge_page() can still fail the
+        * migration and then we want the oldpage to retain the info. But
+        * in that case we also don't need to explicitly clear the info from
+        * the new page, which will be freed.
+        */
+       __set_bit(PAGE_EXT_OWNER, &new_ext->flags);
+}
+
 static ssize_t
 print_page_owner(char __user *buf, size_t count, unsigned long pfn,
                struct page *page, struct page_ext *page_ext)
@@ -100,8 +135,9 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
                return -ENOMEM;
 
        ret = snprintf(kbuf, count,
-                       "Page allocated via order %u, mask 0x%x\n",
-                       page_ext->order, page_ext->gfp_mask);
+                       "Page allocated via order %u, mask %#x(%pGg)\n",
+                       page_ext->order, page_ext->gfp_mask,
+                       &page_ext->gfp_mask);
 
        if (ret >= count)
                goto err;
@@ -110,23 +146,12 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
        pageblock_mt = get_pfnblock_migratetype(page, pfn);
        page_mt  = gfpflags_to_migratetype(page_ext->gfp_mask);
        ret += snprintf(kbuf + ret, count - ret,
-                       "PFN %lu Block %lu type %d %s Flags %s%s%s%s%s%s%s%s%s%s%s%s\n",
+                       "PFN %lu type %s Block %lu type %s Flags %#lx(%pGp)\n",
                        pfn,
+                       migratetype_names[page_mt],
                        pfn >> pageblock_order,
-                       pageblock_mt,
-                       pageblock_mt != page_mt ? "Fallback" : "        ",
-                       PageLocked(page)        ? "K" : " ",
-                       PageError(page)         ? "E" : " ",
-                       PageReferenced(page)    ? "R" : " ",
-                       PageUptodate(page)      ? "U" : " ",
-                       PageDirty(page)         ? "D" : " ",
-                       PageLRU(page)           ? "L" : " ",
-                       PageActive(page)        ? "A" : " ",
-                       PageSlab(page)          ? "S" : " ",
-                       PageWriteback(page)     ? "W" : " ",
-                       PageCompound(page)      ? "C" : " ",
-                       PageSwapCache(page)     ? "B" : " ",
-                       PageMappedToDisk(page)  ? "M" : " ");
+                       migratetype_names[pageblock_mt],
+                       page->flags, &page->flags);
 
        if (ret >= count)
                goto err;
@@ -135,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;
@@ -150,6 +183,30 @@ err:
        return -ENOMEM;
 }
 
+void __dump_page_owner(struct page *page)
+{
+       struct page_ext *page_ext = lookup_page_ext(page);
+       struct stack_trace trace = {
+               .nr_entries = page_ext->nr_entries,
+               .entries = &page_ext->trace_entries[0],
+       };
+       gfp_t gfp_mask = page_ext->gfp_mask;
+       int mt = gfpflags_to_migratetype(gfp_mask);
+
+       if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) {
+               pr_alert("page_owner info is not active (free page?)\n");
+               return;
+       }
+
+       pr_alert("page allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n",
+                page_ext->order, migratetype_names[mt], gfp_mask, &gfp_mask);
+       print_stack_trace(&trace, 0);
+
+       if (page_ext->last_migrate_reason != -1)
+               pr_alert("page has been migrated, last migrate reason: %s\n",
+                       migrate_reason_names[page_ext->last_migrate_reason]);
+}
+
 static ssize_t
 read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 {
@@ -157,7 +214,7 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        struct page *page;
        struct page_ext *page_ext;
 
-       if (!page_owner_inited)
+       if (!static_branch_unlikely(&page_owner_inited))
                return -EINVAL;
 
        page = NULL;
@@ -305,7 +362,7 @@ static int __init pageowner_init(void)
 {
        struct dentry *dentry;
 
-       if (!page_owner_inited) {
+       if (!static_branch_unlikely(&page_owner_inited)) {
                pr_info("page_owner is disabled\n");
                return 0;
        }