]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - mm/page_owner.c
mm, fs: move randomize_stack_top from fs to mm
[linux.git] / mm / page_owner.c
index 925b6f44a444afcddc2a634b8fafb3626eaeb16d..dee931184788399c920646cef75e29e8fd347186 100644 (file)
@@ -24,6 +24,9 @@ struct page_owner {
        short last_migrate_reason;
        gfp_t gfp_mask;
        depot_stack_handle_t handle;
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       depot_stack_handle_t free_handle;
+#endif
 };
 
 static bool page_owner_disabled = true;
@@ -58,15 +61,10 @@ static bool need_page_owner(void)
 static __always_inline depot_stack_handle_t create_dummy_stack(void)
 {
        unsigned long entries[4];
-       struct stack_trace dummy;
+       unsigned int nr_entries;
 
-       dummy.nr_entries = 0;
-       dummy.max_entries = ARRAY_SIZE(entries);
-       dummy.entries = &entries[0];
-       dummy.skip = 0;
-
-       save_stack_trace(&dummy);
-       return depot_save_stack(&dummy, GFP_KERNEL);
+       nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 0);
+       return stack_depot_save(entries, nr_entries, GFP_KERNEL);
 }
 
 static noinline void register_dummy_stack(void)
@@ -107,80 +105,89 @@ static inline struct page_owner *get_page_owner(struct page_ext *page_ext)
        return (void *)page_ext + page_owner_ops.offset;
 }
 
-void __reset_page_owner(struct page *page, unsigned int order)
+static inline bool check_recursive_alloc(unsigned long *entries,
+                                        unsigned int nr_entries,
+                                        unsigned long ip)
 {
-       int i;
-       struct page_ext *page_ext;
-
-       for (i = 0; i < (1 << order); i++) {
-               page_ext = lookup_page_ext(page + i);
-               if (unlikely(!page_ext))
-                       continue;
-               __clear_bit(PAGE_EXT_OWNER, &page_ext->flags);
-       }
-}
-
-static inline bool check_recursive_alloc(struct stack_trace *trace,
-                                       unsigned long ip)
-{
-       int i;
-
-       if (!trace->nr_entries)
-               return false;
+       unsigned int i;
 
-       for (i = 0; i < trace->nr_entries; i++) {
-               if (trace->entries[i] == ip)
+       for (i = 0; i < nr_entries; i++) {
+               if (entries[i] == ip)
                        return true;
        }
-
        return false;
 }
 
 static noinline depot_stack_handle_t save_stack(gfp_t flags)
 {
        unsigned long entries[PAGE_OWNER_STACK_DEPTH];
-       struct stack_trace trace = {
-               .nr_entries = 0,
-               .entries = entries,
-               .max_entries = PAGE_OWNER_STACK_DEPTH,
-               .skip = 2
-       };
        depot_stack_handle_t handle;
+       unsigned int nr_entries;
 
-       save_stack_trace(&trace);
-       if (trace.nr_entries != 0 &&
-           trace.entries[trace.nr_entries-1] == ULONG_MAX)
-               trace.nr_entries--;
+       nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 2);
 
        /*
-        * We need to check recursion here because our request to stackdepot
-        * could trigger memory allocation to save new entry. New memory
-        * allocation would reach here and call depot_save_stack() again
-        * if we don't catch it. There is still not enough memory in stackdepot
-        * so it would try to allocate memory again and loop forever.
+        * We need to check recursion here because our request to
+        * stackdepot could trigger memory allocation to save new
+        * entry. New memory allocation would reach here and call
+        * stack_depot_save_entries() again if we don't catch it. There is
+        * still not enough memory in stackdepot so it would try to
+        * allocate memory again and loop forever.
         */
-       if (check_recursive_alloc(&trace, _RET_IP_))
+       if (check_recursive_alloc(entries, nr_entries, _RET_IP_))
                return dummy_handle;
 
-       handle = depot_save_stack(&trace, flags);
+       handle = stack_depot_save(entries, nr_entries, flags);
        if (!handle)
                handle = failure_handle;
 
        return handle;
 }
 
-static inline void __set_page_owner_handle(struct page_ext *page_ext,
-       depot_stack_handle_t handle, unsigned int order, gfp_t gfp_mask)
+void __reset_page_owner(struct page *page, unsigned int order)
 {
+       int i;
+       struct page_ext *page_ext;
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       depot_stack_handle_t handle = 0;
        struct page_owner *page_owner;
 
-       page_owner = get_page_owner(page_ext);
-       page_owner->handle = handle;
-       page_owner->order = order;
-       page_owner->gfp_mask = gfp_mask;
-       page_owner->last_migrate_reason = -1;
+       if (debug_pagealloc_enabled())
+               handle = save_stack(GFP_NOWAIT | __GFP_NOWARN);
+#endif
+
+       for (i = 0; i < (1 << order); i++) {
+               page_ext = lookup_page_ext(page + i);
+               if (unlikely(!page_ext))
+                       continue;
+               __clear_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags);
+#ifdef CONFIG_DEBUG_PAGEALLOC
+               if (debug_pagealloc_enabled()) {
+                       page_owner = get_page_owner(page_ext);
+                       page_owner->free_handle = handle;
+               }
+#endif
+       }
+}
 
-       __set_bit(PAGE_EXT_OWNER, &page_ext->flags);
+static inline void __set_page_owner_handle(struct page *page,
+       struct page_ext *page_ext, depot_stack_handle_t handle,
+       unsigned int order, gfp_t gfp_mask)
+{
+       struct page_owner *page_owner;
+       int i;
+
+       for (i = 0; i < (1 << order); i++) {
+               page_owner = get_page_owner(page_ext);
+               page_owner->handle = handle;
+               page_owner->order = order;
+               page_owner->gfp_mask = gfp_mask;
+               page_owner->last_migrate_reason = -1;
+               __set_bit(PAGE_EXT_OWNER, &page_ext->flags);
+               __set_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags);
+
+               page_ext = lookup_page_ext(page + i);
+       }
 }
 
 noinline void __set_page_owner(struct page *page, unsigned int order,
@@ -193,7 +200,7 @@ noinline void __set_page_owner(struct page *page, unsigned int order,
                return;
 
        handle = save_stack(gfp_mask);
-       __set_page_owner_handle(page_ext, handle, order, gfp_mask);
+       __set_page_owner_handle(page, page_ext, handle, order, gfp_mask);
 }
 
 void __set_page_owner_migrate_reason(struct page *page, int reason)
@@ -219,8 +226,11 @@ void __split_page_owner(struct page *page, unsigned int order)
 
        page_owner = get_page_owner(page_ext);
        page_owner->order = 0;
-       for (i = 1; i < (1 << order); i++)
-               __copy_page_owner(page, page + i);
+       for (i = 1; i < (1 << order); i++) {
+               page_ext = lookup_page_ext(page + i);
+               page_owner = get_page_owner(page_ext);
+               page_owner->order = 0;
+       }
 }
 
 void __copy_page_owner(struct page *oldpage, struct page *newpage)
@@ -250,6 +260,7 @@ void __copy_page_owner(struct page *oldpage, struct page *newpage)
         * the new page, which will be freed.
         */
        __set_bit(PAGE_EXT_OWNER, &new_ext->flags);
+       __set_bit(PAGE_EXT_OWNER_ACTIVE, &new_ext->flags);
 }
 
 void pagetypeinfo_showmixedcount_print(struct seq_file *m,
@@ -309,7 +320,7 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,
                        if (unlikely(!page_ext))
                                continue;
 
-                       if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
+                       if (!test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags))
                                continue;
 
                        page_owner = get_page_owner(page_ext);
@@ -340,16 +351,10 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
                struct page *page, struct page_owner *page_owner,
                depot_stack_handle_t handle)
 {
-       int ret;
-       int pageblock_mt, page_mt;
+       int ret, pageblock_mt, page_mt;
+       unsigned long *entries;
+       unsigned int nr_entries;
        char *kbuf;
-       unsigned long entries[PAGE_OWNER_STACK_DEPTH];
-       struct stack_trace trace = {
-               .nr_entries = 0,
-               .entries = entries,
-               .max_entries = PAGE_OWNER_STACK_DEPTH,
-               .skip = 0
-       };
 
        count = min_t(size_t, count, PAGE_SIZE);
        kbuf = kmalloc(count, GFP_KERNEL);
@@ -378,8 +383,8 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
        if (ret >= count)
                goto err;
 
-       depot_fetch_stack(handle, &trace);
-       ret += snprint_stack_trace(kbuf + ret, count - ret, &trace, 0);
+       nr_entries = stack_depot_fetch(handle, &entries);
+       ret += stack_trace_snprint(kbuf + ret, count - ret, entries, nr_entries, 0);
        if (ret >= count)
                goto err;
 
@@ -410,14 +415,9 @@ void __dump_page_owner(struct page *page)
 {
        struct page_ext *page_ext = lookup_page_ext(page);
        struct page_owner *page_owner;
-       unsigned long entries[PAGE_OWNER_STACK_DEPTH];
-       struct stack_trace trace = {
-               .nr_entries = 0,
-               .entries = entries,
-               .max_entries = PAGE_OWNER_STACK_DEPTH,
-               .skip = 0
-       };
        depot_stack_handle_t handle;
+       unsigned long *entries;
+       unsigned int nr_entries;
        gfp_t gfp_mask;
        int mt;
 
@@ -431,20 +431,36 @@ void __dump_page_owner(struct page *page)
        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");
+               pr_alert("page_owner info is not present (never set?)\n");
                return;
        }
 
+       if (test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags))
+               pr_alert("page_owner tracks the page as allocated\n");
+       else
+               pr_alert("page_owner tracks the page as freed\n");
+
+       pr_alert("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n",
+                page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask);
+
        handle = READ_ONCE(page_owner->handle);
        if (!handle) {
-               pr_alert("page_owner info is not active (free page?)\n");
-               return;
+               pr_alert("page_owner allocation stack trace missing\n");
+       } else {
+               nr_entries = stack_depot_fetch(handle, &entries);
+               stack_trace_print(entries, nr_entries, 0);
        }
 
-       depot_fetch_stack(handle, &trace);
-       pr_alert("page allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n",
-                page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask);
-       print_stack_trace(&trace, 0);
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       handle = READ_ONCE(page_owner->free_handle);
+       if (!handle) {
+               pr_alert("page_owner free stack trace missing\n");
+       } else {
+               nr_entries = stack_depot_fetch(handle, &entries);
+               pr_alert("page last free stack trace:\n");
+               stack_trace_print(entries, nr_entries, 0);
+       }
+#endif
 
        if (page_owner->last_migrate_reason != -1)
                pr_alert("page has been migrated, last migrate reason: %s\n",
@@ -507,8 +523,22 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
                if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
                        continue;
 
+               /*
+                * Although we do have the info about past allocation of free
+                * pages, it's not relevant for current memory usage.
+                */
+               if (!test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags))
+                       continue;
+
                page_owner = get_page_owner(page_ext);
 
+               /*
+                * Don't print "tail" pages of high-order allocations as that
+                * would inflate the stats.
+                */
+               if (!IS_ALIGNED(pfn, 1 << page_owner->order))
+                       continue;
+
                /*
                 * Access to page_ext->handle isn't synchronous so we should
                 * be careful to access it.
@@ -588,7 +618,8 @@ static void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone)
                                continue;
 
                        /* Found early allocated page */
-                       __set_page_owner_handle(page_ext, early_handle, 0, 0);
+                       __set_page_owner_handle(page, page_ext, early_handle,
+                                               0, 0);
                        count++;
                }
                cond_resched();