diff options
Diffstat (limited to 'mm/page_owner.c')
-rw-r--r-- | mm/page_owner.c | 123 |
1 files changed, 90 insertions, 33 deletions
diff --git a/mm/page_owner.c b/mm/page_owner.c index addcbb2ae4e4..dee931184788 100644 --- a/mm/page_owner.c +++ b/mm/page_owner.c | |||
@@ -24,6 +24,9 @@ struct page_owner { | |||
24 | short last_migrate_reason; | 24 | short last_migrate_reason; |
25 | gfp_t gfp_mask; | 25 | gfp_t gfp_mask; |
26 | depot_stack_handle_t handle; | 26 | depot_stack_handle_t handle; |
27 | #ifdef CONFIG_DEBUG_PAGEALLOC | ||
28 | depot_stack_handle_t free_handle; | ||
29 | #endif | ||
27 | }; | 30 | }; |
28 | 31 | ||
29 | static bool page_owner_disabled = true; | 32 | static bool page_owner_disabled = true; |
@@ -102,19 +105,6 @@ static inline struct page_owner *get_page_owner(struct page_ext *page_ext) | |||
102 | return (void *)page_ext + page_owner_ops.offset; | 105 | return (void *)page_ext + page_owner_ops.offset; |
103 | } | 106 | } |
104 | 107 | ||
105 | void __reset_page_owner(struct page *page, unsigned int order) | ||
106 | { | ||
107 | int i; | ||
108 | struct page_ext *page_ext; | ||
109 | |||
110 | for (i = 0; i < (1 << order); i++) { | ||
111 | page_ext = lookup_page_ext(page + i); | ||
112 | if (unlikely(!page_ext)) | ||
113 | continue; | ||
114 | __clear_bit(PAGE_EXT_OWNER, &page_ext->flags); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static inline bool check_recursive_alloc(unsigned long *entries, | 108 | static inline bool check_recursive_alloc(unsigned long *entries, |
119 | unsigned int nr_entries, | 109 | unsigned int nr_entries, |
120 | unsigned long ip) | 110 | unsigned long ip) |
@@ -154,18 +144,50 @@ static noinline depot_stack_handle_t save_stack(gfp_t flags) | |||
154 | return handle; | 144 | return handle; |
155 | } | 145 | } |
156 | 146 | ||
157 | static inline void __set_page_owner_handle(struct page_ext *page_ext, | 147 | void __reset_page_owner(struct page *page, unsigned int order) |
158 | depot_stack_handle_t handle, unsigned int order, gfp_t gfp_mask) | ||
159 | { | 148 | { |
149 | int i; | ||
150 | struct page_ext *page_ext; | ||
151 | #ifdef CONFIG_DEBUG_PAGEALLOC | ||
152 | depot_stack_handle_t handle = 0; | ||
160 | struct page_owner *page_owner; | 153 | struct page_owner *page_owner; |
161 | 154 | ||
162 | page_owner = get_page_owner(page_ext); | 155 | if (debug_pagealloc_enabled()) |
163 | page_owner->handle = handle; | 156 | handle = save_stack(GFP_NOWAIT | __GFP_NOWARN); |
164 | page_owner->order = order; | 157 | #endif |
165 | page_owner->gfp_mask = gfp_mask; | ||
166 | page_owner->last_migrate_reason = -1; | ||
167 | 158 | ||
168 | __set_bit(PAGE_EXT_OWNER, &page_ext->flags); | 159 | for (i = 0; i < (1 << order); i++) { |
160 | page_ext = lookup_page_ext(page + i); | ||
161 | if (unlikely(!page_ext)) | ||
162 | continue; | ||
163 | __clear_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags); | ||
164 | #ifdef CONFIG_DEBUG_PAGEALLOC | ||
165 | if (debug_pagealloc_enabled()) { | ||
166 | page_owner = get_page_owner(page_ext); | ||
167 | page_owner->free_handle = handle; | ||
168 | } | ||
169 | #endif | ||
170 | } | ||
171 | } | ||
172 | |||
173 | static inline void __set_page_owner_handle(struct page *page, | ||
174 | struct page_ext *page_ext, depot_stack_handle_t handle, | ||
175 | unsigned int order, gfp_t gfp_mask) | ||
176 | { | ||
177 | struct page_owner *page_owner; | ||
178 | int i; | ||
179 | |||
180 | for (i = 0; i < (1 << order); i++) { | ||
181 | page_owner = get_page_owner(page_ext); | ||
182 | page_owner->handle = handle; | ||
183 | page_owner->order = order; | ||
184 | page_owner->gfp_mask = gfp_mask; | ||
185 | page_owner->last_migrate_reason = -1; | ||
186 | __set_bit(PAGE_EXT_OWNER, &page_ext->flags); | ||
187 | __set_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags); | ||
188 | |||
189 | page_ext = lookup_page_ext(page + i); | ||
190 | } | ||
169 | } | 191 | } |
170 | 192 | ||
171 | noinline void __set_page_owner(struct page *page, unsigned int order, | 193 | noinline void __set_page_owner(struct page *page, unsigned int order, |
@@ -178,7 +200,7 @@ noinline void __set_page_owner(struct page *page, unsigned int order, | |||
178 | return; | 200 | return; |
179 | 201 | ||
180 | handle = save_stack(gfp_mask); | 202 | handle = save_stack(gfp_mask); |
181 | __set_page_owner_handle(page_ext, handle, order, gfp_mask); | 203 | __set_page_owner_handle(page, page_ext, handle, order, gfp_mask); |
182 | } | 204 | } |
183 | 205 | ||
184 | void __set_page_owner_migrate_reason(struct page *page, int reason) | 206 | void __set_page_owner_migrate_reason(struct page *page, int reason) |
@@ -204,8 +226,11 @@ void __split_page_owner(struct page *page, unsigned int order) | |||
204 | 226 | ||
205 | page_owner = get_page_owner(page_ext); | 227 | page_owner = get_page_owner(page_ext); |
206 | page_owner->order = 0; | 228 | page_owner->order = 0; |
207 | for (i = 1; i < (1 << order); i++) | 229 | for (i = 1; i < (1 << order); i++) { |
208 | __copy_page_owner(page, page + i); | 230 | page_ext = lookup_page_ext(page + i); |
231 | page_owner = get_page_owner(page_ext); | ||
232 | page_owner->order = 0; | ||
233 | } | ||
209 | } | 234 | } |
210 | 235 | ||
211 | void __copy_page_owner(struct page *oldpage, struct page *newpage) | 236 | void __copy_page_owner(struct page *oldpage, struct page *newpage) |
@@ -235,6 +260,7 @@ void __copy_page_owner(struct page *oldpage, struct page *newpage) | |||
235 | * the new page, which will be freed. | 260 | * the new page, which will be freed. |
236 | */ | 261 | */ |
237 | __set_bit(PAGE_EXT_OWNER, &new_ext->flags); | 262 | __set_bit(PAGE_EXT_OWNER, &new_ext->flags); |
263 | __set_bit(PAGE_EXT_OWNER_ACTIVE, &new_ext->flags); | ||
238 | } | 264 | } |
239 | 265 | ||
240 | void pagetypeinfo_showmixedcount_print(struct seq_file *m, | 266 | void pagetypeinfo_showmixedcount_print(struct seq_file *m, |
@@ -294,7 +320,7 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m, | |||
294 | if (unlikely(!page_ext)) | 320 | if (unlikely(!page_ext)) |
295 | continue; | 321 | continue; |
296 | 322 | ||
297 | if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) | 323 | if (!test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags)) |
298 | continue; | 324 | continue; |
299 | 325 | ||
300 | page_owner = get_page_owner(page_ext); | 326 | page_owner = get_page_owner(page_ext); |
@@ -405,20 +431,36 @@ void __dump_page_owner(struct page *page) | |||
405 | mt = gfpflags_to_migratetype(gfp_mask); | 431 | mt = gfpflags_to_migratetype(gfp_mask); |
406 | 432 | ||
407 | if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) { | 433 | if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) { |
408 | pr_alert("page_owner info is not active (free page?)\n"); | 434 | pr_alert("page_owner info is not present (never set?)\n"); |
409 | return; | 435 | return; |
410 | } | 436 | } |
411 | 437 | ||
438 | if (test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags)) | ||
439 | pr_alert("page_owner tracks the page as allocated\n"); | ||
440 | else | ||
441 | pr_alert("page_owner tracks the page as freed\n"); | ||
442 | |||
443 | pr_alert("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n", | ||
444 | page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask); | ||
445 | |||
412 | handle = READ_ONCE(page_owner->handle); | 446 | handle = READ_ONCE(page_owner->handle); |
413 | if (!handle) { | 447 | if (!handle) { |
414 | pr_alert("page_owner info is not active (free page?)\n"); | 448 | pr_alert("page_owner allocation stack trace missing\n"); |
415 | return; | 449 | } else { |
450 | nr_entries = stack_depot_fetch(handle, &entries); | ||
451 | stack_trace_print(entries, nr_entries, 0); | ||
416 | } | 452 | } |
417 | 453 | ||
418 | nr_entries = stack_depot_fetch(handle, &entries); | 454 | #ifdef CONFIG_DEBUG_PAGEALLOC |
419 | pr_alert("page allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n", | 455 | handle = READ_ONCE(page_owner->free_handle); |
420 | page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask); | 456 | if (!handle) { |
421 | stack_trace_print(entries, nr_entries, 0); | 457 | pr_alert("page_owner free stack trace missing\n"); |
458 | } else { | ||
459 | nr_entries = stack_depot_fetch(handle, &entries); | ||
460 | pr_alert("page last free stack trace:\n"); | ||
461 | stack_trace_print(entries, nr_entries, 0); | ||
462 | } | ||
463 | #endif | ||
422 | 464 | ||
423 | if (page_owner->last_migrate_reason != -1) | 465 | if (page_owner->last_migrate_reason != -1) |
424 | pr_alert("page has been migrated, last migrate reason: %s\n", | 466 | pr_alert("page has been migrated, last migrate reason: %s\n", |
@@ -481,9 +523,23 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
481 | if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) | 523 | if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) |
482 | continue; | 524 | continue; |
483 | 525 | ||
526 | /* | ||
527 | * Although we do have the info about past allocation of free | ||
528 | * pages, it's not relevant for current memory usage. | ||
529 | */ | ||
530 | if (!test_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags)) | ||
531 | continue; | ||
532 | |||
484 | page_owner = get_page_owner(page_ext); | 533 | page_owner = get_page_owner(page_ext); |
485 | 534 | ||
486 | /* | 535 | /* |
536 | * Don't print "tail" pages of high-order allocations as that | ||
537 | * would inflate the stats. | ||
538 | */ | ||
539 | if (!IS_ALIGNED(pfn, 1 << page_owner->order)) | ||
540 | continue; | ||
541 | |||
542 | /* | ||
487 | * Access to page_ext->handle isn't synchronous so we should | 543 | * Access to page_ext->handle isn't synchronous so we should |
488 | * be careful to access it. | 544 | * be careful to access it. |
489 | */ | 545 | */ |
@@ -562,7 +618,8 @@ static void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone) | |||
562 | continue; | 618 | continue; |
563 | 619 | ||
564 | /* Found early allocated page */ | 620 | /* Found early allocated page */ |
565 | __set_page_owner_handle(page_ext, early_handle, 0, 0); | 621 | __set_page_owner_handle(page, page_ext, early_handle, |
622 | 0, 0); | ||
566 | count++; | 623 | count++; |
567 | } | 624 | } |
568 | cond_resched(); | 625 | cond_resched(); |