History

mm, page_owner: keep owner info when freeing the page

For debugging purposes it might be useful to keep the owner info even after page has been freed, and include it in e.g. dump_page() when detecting a bad page state. For that, change the PAGE_EXT_OWNER flag meaning to "page owner info has been set at least once" and add new PAGE_EXT_OWNER_ACTIVE for tracking whether page is supposed to be currently tracked allocated or free. Adjust dump_page() accordingly, distinguishing free and allocated pages. In the page_owner debugfs file, keep printing only allocated pages so that existing scripts are not confused, and also because free pages are irrelevant for the memory statistics or leak detection that's the typical use case of the file, anyway.

Vlastimil Babka committed 7 years ago

Commit 37389161 parent 7e2f2a02 files · +25 −10Browse files

Unified · Split

2 files
  1. include/linux/page_ext.h +1 −0
  2. mm/page_owner.c +24 −10
include/linux/page_ext.h+1−0
@@ -18,6 +18,7 @@ struct page_ext_operations { · 17 unmodified lines
18 18
19enum page_ext_flags {19enum page_ext_flags {
20 PAGE_EXT_OWNER,20 PAGE_EXT_OWNER,
21 PAGE_EXT_OWNER_ACTIVE,
21#if defined(CONFIG_IDLE_PAGE_TRACKING) && !defined(CONFIG_64BIT)22#if defined(CONFIG_IDLE_PAGE_TRACKING) && !defined(CONFIG_64BIT)
22 PAGE_EXT_YOUNG,23 PAGE_EXT_YOUNG,
23 PAGE_EXT_IDLE,24 PAGE_EXT_IDLE,
mm/page_owner.c+24−10
@@ -111,7 +111,7 @@ void __reset_page_owner(struct page *page, unsigned int order) · 110 unmodified lines
111 page_ext = lookup_page_ext(page + i);111 page_ext = lookup_page_ext(page + i);
112 if (unlikely(!page_ext))112 if (unlikely(!page_ext))
113 continue;113 continue;
114 __clear_bit(PAGE_EXT_OWNER, &page_ext->flags);114 __clear_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags);
115 }115 }
116}116}
117 117
@@ -168,6 +168,7 @@ static inline void __set_page_owner_handle(struct page *page, · 50 unmodified lines
168 page_owner->gfp_mask = gfp_mask;168 page_owner->gfp_mask = gfp_mask;
169 page_owner->last_migrate_reason = -1;169 page_owner->last_migrate_reason = -1;
170 __set_bit(PAGE_EXT_OWNER, &page_ext->flags);170 __set_bit(PAGE_EXT_OWNER, &page_ext->flags);
171 __set_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags);
171 172
172 page_ext = lookup_page_ext(page + i);173 page_ext = lookup_page_ext(page + i);
173 }174 }
@@ -243,6 +244,7 @@ void __copy_page_owner(struct page *oldpage, struct page *newpage) · 69 unmodified lines
243 * the new page, which will be freed.244 * the new page, which will be freed.
244 */245 */
245 __set_bit(PAGE_EXT_OWNER, &new_ext->flags);246 __set_bit(PAGE_EXT_OWNER, &new_ext->flags);
247 __set_bit(PAGE_EXT_OWNER_ACTIVE, &new_ext->flags);
246}248}
247 249
248void pagetypeinfo_showmixedcount_print(struct seq_file *m,250void pagetypeinfo_showmixedcount_print(struct seq_file *m,
@@ -302,7 +304,7 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m, · 53 unmodified lines
302 if (unlikely(!page_ext))304 if (unlikely(!page_ext))
303 continue;305 continue;
304 306
305 if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))307 if (!test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags))
306 continue;308 continue;
307 309
308 page_owner = get_page_owner(page_ext);310 page_owner = get_page_owner(page_ext);
@@ -413,21 +415,26 @@ void __dump_page_owner(struct page *page) · 104 unmodified lines
413 mt = gfpflags_to_migratetype(gfp_mask);415 mt = gfpflags_to_migratetype(gfp_mask);
414 416
415 if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) {417 if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) {
416 pr_alert("page_owner info is not active (free page?)\n");418 pr_alert("page_owner info is not present (never set?)\n");
417 return;419 return;
418 }420 }
419 421
422 if (test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags))
423 pr_alert("page_owner tracks the page as allocated\n");
424 else
425 pr_alert("page_owner tracks the page as freed\n");
426
427 pr_alert("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n",
428 page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask);
429
420 handle = READ_ONCE(page_owner->handle);430 handle = READ_ONCE(page_owner->handle);
421 if (!handle) {431 if (!handle) {
422 pr_alert("page_owner info is not active (free page?)\n");432 pr_alert("page_owner allocation stack trace missing\n");
423 return;433 } else {
434 nr_entries = stack_depot_fetch(handle, &entries);
435 stack_trace_print(entries, nr_entries, 0);
424 }436 }
425 437
426 nr_entries = stack_depot_fetch(handle, &entries);
427 pr_alert("page allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n",
428 page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask);
429 stack_trace_print(entries, nr_entries, 0);
430
431 if (page_owner->last_migrate_reason != -1)438 if (page_owner->last_migrate_reason != -1)
432 pr_alert("page has been migrated, last migrate reason: %s\n",439 pr_alert("page has been migrated, last migrate reason: %s\n",
433 migrate_reason_names[page_owner->last_migrate_reason]);440 migrate_reason_names[page_owner->last_migrate_reason]);
@@ -489,6 +496,13 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos) · 55 unmodified lines
489 if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))496 if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
490 continue;497 continue;
491 498
499 /*
500 * Although we do have the info about past allocation of free
501 * pages, it's not relevant for current memory usage.
502 */
503 if (!test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags))
504 continue;
505
492 page_owner = get_page_owner(page_ext);506 page_owner = get_page_owner(page_ext);
493 507
494 /*508 /*

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t