xref: /linux/mm/page_owner.c (revision 7ebdfaa52d15b947503f76474477f92854796d96)
148c96a36SJoonsoo Kim #include <linux/debugfs.h>
248c96a36SJoonsoo Kim #include <linux/mm.h>
348c96a36SJoonsoo Kim #include <linux/slab.h>
448c96a36SJoonsoo Kim #include <linux/uaccess.h>
548c96a36SJoonsoo Kim #include <linux/bootmem.h>
648c96a36SJoonsoo Kim #include <linux/stacktrace.h>
748c96a36SJoonsoo Kim #include <linux/page_owner.h>
848c96a36SJoonsoo Kim #include "internal.h"
948c96a36SJoonsoo Kim 
1048c96a36SJoonsoo Kim static bool page_owner_disabled = true;
1148c96a36SJoonsoo Kim bool page_owner_inited __read_mostly;
1248c96a36SJoonsoo Kim 
13*61cf5febSJoonsoo Kim static void init_early_allocated_pages(void);
14*61cf5febSJoonsoo Kim 
1548c96a36SJoonsoo Kim static int early_page_owner_param(char *buf)
1648c96a36SJoonsoo Kim {
1748c96a36SJoonsoo Kim 	if (!buf)
1848c96a36SJoonsoo Kim 		return -EINVAL;
1948c96a36SJoonsoo Kim 
2048c96a36SJoonsoo Kim 	if (strcmp(buf, "on") == 0)
2148c96a36SJoonsoo Kim 		page_owner_disabled = false;
2248c96a36SJoonsoo Kim 
2348c96a36SJoonsoo Kim 	return 0;
2448c96a36SJoonsoo Kim }
2548c96a36SJoonsoo Kim early_param("page_owner", early_page_owner_param);
2648c96a36SJoonsoo Kim 
2748c96a36SJoonsoo Kim static bool need_page_owner(void)
2848c96a36SJoonsoo Kim {
2948c96a36SJoonsoo Kim 	if (page_owner_disabled)
3048c96a36SJoonsoo Kim 		return false;
3148c96a36SJoonsoo Kim 
3248c96a36SJoonsoo Kim 	return true;
3348c96a36SJoonsoo Kim }
3448c96a36SJoonsoo Kim 
3548c96a36SJoonsoo Kim static void init_page_owner(void)
3648c96a36SJoonsoo Kim {
3748c96a36SJoonsoo Kim 	if (page_owner_disabled)
3848c96a36SJoonsoo Kim 		return;
3948c96a36SJoonsoo Kim 
4048c96a36SJoonsoo Kim 	page_owner_inited = true;
41*61cf5febSJoonsoo Kim 	init_early_allocated_pages();
4248c96a36SJoonsoo Kim }
4348c96a36SJoonsoo Kim 
4448c96a36SJoonsoo Kim struct page_ext_operations page_owner_ops = {
4548c96a36SJoonsoo Kim 	.need = need_page_owner,
4648c96a36SJoonsoo Kim 	.init = init_page_owner,
4748c96a36SJoonsoo Kim };
4848c96a36SJoonsoo Kim 
4948c96a36SJoonsoo Kim void __reset_page_owner(struct page *page, unsigned int order)
5048c96a36SJoonsoo Kim {
5148c96a36SJoonsoo Kim 	int i;
5248c96a36SJoonsoo Kim 	struct page_ext *page_ext;
5348c96a36SJoonsoo Kim 
5448c96a36SJoonsoo Kim 	for (i = 0; i < (1 << order); i++) {
5548c96a36SJoonsoo Kim 		page_ext = lookup_page_ext(page + i);
5648c96a36SJoonsoo Kim 		__clear_bit(PAGE_EXT_OWNER, &page_ext->flags);
5748c96a36SJoonsoo Kim 	}
5848c96a36SJoonsoo Kim }
5948c96a36SJoonsoo Kim 
6048c96a36SJoonsoo Kim void __set_page_owner(struct page *page, unsigned int order, gfp_t gfp_mask)
6148c96a36SJoonsoo Kim {
6248c96a36SJoonsoo Kim 	struct page_ext *page_ext;
6348c96a36SJoonsoo Kim 	struct stack_trace *trace;
6448c96a36SJoonsoo Kim 
6548c96a36SJoonsoo Kim 	page_ext = lookup_page_ext(page);
6648c96a36SJoonsoo Kim 
6748c96a36SJoonsoo Kim 	trace = &page_ext->trace;
6848c96a36SJoonsoo Kim 	trace->nr_entries = 0;
6948c96a36SJoonsoo Kim 	trace->max_entries = ARRAY_SIZE(page_ext->trace_entries);
7048c96a36SJoonsoo Kim 	trace->entries = &page_ext->trace_entries[0];
7148c96a36SJoonsoo Kim 	trace->skip = 3;
7248c96a36SJoonsoo Kim 	save_stack_trace(&page_ext->trace);
7348c96a36SJoonsoo Kim 
7448c96a36SJoonsoo Kim 	page_ext->order = order;
7548c96a36SJoonsoo Kim 	page_ext->gfp_mask = gfp_mask;
7648c96a36SJoonsoo Kim 
7748c96a36SJoonsoo Kim 	__set_bit(PAGE_EXT_OWNER, &page_ext->flags);
7848c96a36SJoonsoo Kim }
7948c96a36SJoonsoo Kim 
8048c96a36SJoonsoo Kim static ssize_t
8148c96a36SJoonsoo Kim print_page_owner(char __user *buf, size_t count, unsigned long pfn,
8248c96a36SJoonsoo Kim 		struct page *page, struct page_ext *page_ext)
8348c96a36SJoonsoo Kim {
8448c96a36SJoonsoo Kim 	int ret;
8548c96a36SJoonsoo Kim 	int pageblock_mt, page_mt;
8648c96a36SJoonsoo Kim 	char *kbuf;
8748c96a36SJoonsoo Kim 
8848c96a36SJoonsoo Kim 	kbuf = kmalloc(count, GFP_KERNEL);
8948c96a36SJoonsoo Kim 	if (!kbuf)
9048c96a36SJoonsoo Kim 		return -ENOMEM;
9148c96a36SJoonsoo Kim 
9248c96a36SJoonsoo Kim 	ret = snprintf(kbuf, count,
9348c96a36SJoonsoo Kim 			"Page allocated via order %u, mask 0x%x\n",
9448c96a36SJoonsoo Kim 			page_ext->order, page_ext->gfp_mask);
9548c96a36SJoonsoo Kim 
9648c96a36SJoonsoo Kim 	if (ret >= count)
9748c96a36SJoonsoo Kim 		goto err;
9848c96a36SJoonsoo Kim 
9948c96a36SJoonsoo Kim 	/* Print information relevant to grouping pages by mobility */
10048c96a36SJoonsoo Kim 	pageblock_mt = get_pfnblock_migratetype(page, pfn);
10148c96a36SJoonsoo Kim 	page_mt  = gfpflags_to_migratetype(page_ext->gfp_mask);
10248c96a36SJoonsoo Kim 	ret += snprintf(kbuf + ret, count - ret,
10348c96a36SJoonsoo Kim 			"PFN %lu Block %lu type %d %s Flags %s%s%s%s%s%s%s%s%s%s%s%s\n",
10448c96a36SJoonsoo Kim 			pfn,
10548c96a36SJoonsoo Kim 			pfn >> pageblock_order,
10648c96a36SJoonsoo Kim 			pageblock_mt,
10748c96a36SJoonsoo Kim 			pageblock_mt != page_mt ? "Fallback" : "        ",
10848c96a36SJoonsoo Kim 			PageLocked(page)	? "K" : " ",
10948c96a36SJoonsoo Kim 			PageError(page)		? "E" : " ",
11048c96a36SJoonsoo Kim 			PageReferenced(page)	? "R" : " ",
11148c96a36SJoonsoo Kim 			PageUptodate(page)	? "U" : " ",
11248c96a36SJoonsoo Kim 			PageDirty(page)		? "D" : " ",
11348c96a36SJoonsoo Kim 			PageLRU(page)		? "L" : " ",
11448c96a36SJoonsoo Kim 			PageActive(page)	? "A" : " ",
11548c96a36SJoonsoo Kim 			PageSlab(page)		? "S" : " ",
11648c96a36SJoonsoo Kim 			PageWriteback(page)	? "W" : " ",
11748c96a36SJoonsoo Kim 			PageCompound(page)	? "C" : " ",
11848c96a36SJoonsoo Kim 			PageSwapCache(page)	? "B" : " ",
11948c96a36SJoonsoo Kim 			PageMappedToDisk(page)	? "M" : " ");
12048c96a36SJoonsoo Kim 
12148c96a36SJoonsoo Kim 	if (ret >= count)
12248c96a36SJoonsoo Kim 		goto err;
12348c96a36SJoonsoo Kim 
12448c96a36SJoonsoo Kim 	ret += snprint_stack_trace(kbuf + ret, count - ret,
12548c96a36SJoonsoo Kim 					&page_ext->trace, 0);
12648c96a36SJoonsoo Kim 	if (ret >= count)
12748c96a36SJoonsoo Kim 		goto err;
12848c96a36SJoonsoo Kim 
12948c96a36SJoonsoo Kim 	ret += snprintf(kbuf + ret, count - ret, "\n");
13048c96a36SJoonsoo Kim 	if (ret >= count)
13148c96a36SJoonsoo Kim 		goto err;
13248c96a36SJoonsoo Kim 
13348c96a36SJoonsoo Kim 	if (copy_to_user(buf, kbuf, ret))
13448c96a36SJoonsoo Kim 		ret = -EFAULT;
13548c96a36SJoonsoo Kim 
13648c96a36SJoonsoo Kim 	kfree(kbuf);
13748c96a36SJoonsoo Kim 	return ret;
13848c96a36SJoonsoo Kim 
13948c96a36SJoonsoo Kim err:
14048c96a36SJoonsoo Kim 	kfree(kbuf);
14148c96a36SJoonsoo Kim 	return -ENOMEM;
14248c96a36SJoonsoo Kim }
14348c96a36SJoonsoo Kim 
14448c96a36SJoonsoo Kim static ssize_t
14548c96a36SJoonsoo Kim read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
14648c96a36SJoonsoo Kim {
14748c96a36SJoonsoo Kim 	unsigned long pfn;
14848c96a36SJoonsoo Kim 	struct page *page;
14948c96a36SJoonsoo Kim 	struct page_ext *page_ext;
15048c96a36SJoonsoo Kim 
15148c96a36SJoonsoo Kim 	if (!page_owner_inited)
15248c96a36SJoonsoo Kim 		return -EINVAL;
15348c96a36SJoonsoo Kim 
15448c96a36SJoonsoo Kim 	page = NULL;
15548c96a36SJoonsoo Kim 	pfn = min_low_pfn + *ppos;
15648c96a36SJoonsoo Kim 
15748c96a36SJoonsoo Kim 	/* Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area */
15848c96a36SJoonsoo Kim 	while (!pfn_valid(pfn) && (pfn & (MAX_ORDER_NR_PAGES - 1)) != 0)
15948c96a36SJoonsoo Kim 		pfn++;
16048c96a36SJoonsoo Kim 
16148c96a36SJoonsoo Kim 	drain_all_pages(NULL);
16248c96a36SJoonsoo Kim 
16348c96a36SJoonsoo Kim 	/* Find an allocated page */
16448c96a36SJoonsoo Kim 	for (; pfn < max_pfn; pfn++) {
16548c96a36SJoonsoo Kim 		/*
16648c96a36SJoonsoo Kim 		 * If the new page is in a new MAX_ORDER_NR_PAGES area,
16748c96a36SJoonsoo Kim 		 * validate the area as existing, skip it if not
16848c96a36SJoonsoo Kim 		 */
16948c96a36SJoonsoo Kim 		if ((pfn & (MAX_ORDER_NR_PAGES - 1)) == 0 && !pfn_valid(pfn)) {
17048c96a36SJoonsoo Kim 			pfn += MAX_ORDER_NR_PAGES - 1;
17148c96a36SJoonsoo Kim 			continue;
17248c96a36SJoonsoo Kim 		}
17348c96a36SJoonsoo Kim 
17448c96a36SJoonsoo Kim 		/* Check for holes within a MAX_ORDER area */
17548c96a36SJoonsoo Kim 		if (!pfn_valid_within(pfn))
17648c96a36SJoonsoo Kim 			continue;
17748c96a36SJoonsoo Kim 
17848c96a36SJoonsoo Kim 		page = pfn_to_page(pfn);
17948c96a36SJoonsoo Kim 		if (PageBuddy(page)) {
18048c96a36SJoonsoo Kim 			unsigned long freepage_order = page_order_unsafe(page);
18148c96a36SJoonsoo Kim 
18248c96a36SJoonsoo Kim 			if (freepage_order < MAX_ORDER)
18348c96a36SJoonsoo Kim 				pfn += (1UL << freepage_order) - 1;
18448c96a36SJoonsoo Kim 			continue;
18548c96a36SJoonsoo Kim 		}
18648c96a36SJoonsoo Kim 
18748c96a36SJoonsoo Kim 		page_ext = lookup_page_ext(page);
18848c96a36SJoonsoo Kim 
18948c96a36SJoonsoo Kim 		/*
190*61cf5febSJoonsoo Kim 		 * Some pages could be missed by concurrent allocation or free,
191*61cf5febSJoonsoo Kim 		 * because we don't hold the zone lock.
19248c96a36SJoonsoo Kim 		 */
19348c96a36SJoonsoo Kim 		if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
19448c96a36SJoonsoo Kim 			continue;
19548c96a36SJoonsoo Kim 
19648c96a36SJoonsoo Kim 		/* Record the next PFN to read in the file offset */
19748c96a36SJoonsoo Kim 		*ppos = (pfn - min_low_pfn) + 1;
19848c96a36SJoonsoo Kim 
19948c96a36SJoonsoo Kim 		return print_page_owner(buf, count, pfn, page, page_ext);
20048c96a36SJoonsoo Kim 	}
20148c96a36SJoonsoo Kim 
20248c96a36SJoonsoo Kim 	return 0;
20348c96a36SJoonsoo Kim }
20448c96a36SJoonsoo Kim 
205*61cf5febSJoonsoo Kim static void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone)
206*61cf5febSJoonsoo Kim {
207*61cf5febSJoonsoo Kim 	struct page *page;
208*61cf5febSJoonsoo Kim 	struct page_ext *page_ext;
209*61cf5febSJoonsoo Kim 	unsigned long pfn = zone->zone_start_pfn, block_end_pfn;
210*61cf5febSJoonsoo Kim 	unsigned long end_pfn = pfn + zone->spanned_pages;
211*61cf5febSJoonsoo Kim 	unsigned long count = 0;
212*61cf5febSJoonsoo Kim 
213*61cf5febSJoonsoo Kim 	/* Scan block by block. First and last block may be incomplete */
214*61cf5febSJoonsoo Kim 	pfn = zone->zone_start_pfn;
215*61cf5febSJoonsoo Kim 
216*61cf5febSJoonsoo Kim 	/*
217*61cf5febSJoonsoo Kim 	 * Walk the zone in pageblock_nr_pages steps. If a page block spans
218*61cf5febSJoonsoo Kim 	 * a zone boundary, it will be double counted between zones. This does
219*61cf5febSJoonsoo Kim 	 * not matter as the mixed block count will still be correct
220*61cf5febSJoonsoo Kim 	 */
221*61cf5febSJoonsoo Kim 	for (; pfn < end_pfn; ) {
222*61cf5febSJoonsoo Kim 		if (!pfn_valid(pfn)) {
223*61cf5febSJoonsoo Kim 			pfn = ALIGN(pfn + 1, MAX_ORDER_NR_PAGES);
224*61cf5febSJoonsoo Kim 			continue;
225*61cf5febSJoonsoo Kim 		}
226*61cf5febSJoonsoo Kim 
227*61cf5febSJoonsoo Kim 		block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
228*61cf5febSJoonsoo Kim 		block_end_pfn = min(block_end_pfn, end_pfn);
229*61cf5febSJoonsoo Kim 
230*61cf5febSJoonsoo Kim 		page = pfn_to_page(pfn);
231*61cf5febSJoonsoo Kim 
232*61cf5febSJoonsoo Kim 		for (; pfn < block_end_pfn; pfn++) {
233*61cf5febSJoonsoo Kim 			if (!pfn_valid_within(pfn))
234*61cf5febSJoonsoo Kim 				continue;
235*61cf5febSJoonsoo Kim 
236*61cf5febSJoonsoo Kim 			page = pfn_to_page(pfn);
237*61cf5febSJoonsoo Kim 
238*61cf5febSJoonsoo Kim 			/*
239*61cf5febSJoonsoo Kim 			 * We are safe to check buddy flag and order, because
240*61cf5febSJoonsoo Kim 			 * this is init stage and only single thread runs.
241*61cf5febSJoonsoo Kim 			 */
242*61cf5febSJoonsoo Kim 			if (PageBuddy(page)) {
243*61cf5febSJoonsoo Kim 				pfn += (1UL << page_order(page)) - 1;
244*61cf5febSJoonsoo Kim 				continue;
245*61cf5febSJoonsoo Kim 			}
246*61cf5febSJoonsoo Kim 
247*61cf5febSJoonsoo Kim 			if (PageReserved(page))
248*61cf5febSJoonsoo Kim 				continue;
249*61cf5febSJoonsoo Kim 
250*61cf5febSJoonsoo Kim 			page_ext = lookup_page_ext(page);
251*61cf5febSJoonsoo Kim 
252*61cf5febSJoonsoo Kim 			/* Maybe overraping zone */
253*61cf5febSJoonsoo Kim 			if (test_bit(PAGE_EXT_OWNER, &page_ext->flags))
254*61cf5febSJoonsoo Kim 				continue;
255*61cf5febSJoonsoo Kim 
256*61cf5febSJoonsoo Kim 			/* Found early allocated page */
257*61cf5febSJoonsoo Kim 			set_page_owner(page, 0, 0);
258*61cf5febSJoonsoo Kim 			count++;
259*61cf5febSJoonsoo Kim 		}
260*61cf5febSJoonsoo Kim 	}
261*61cf5febSJoonsoo Kim 
262*61cf5febSJoonsoo Kim 	pr_info("Node %d, zone %8s: page owner found early allocated %lu pages\n",
263*61cf5febSJoonsoo Kim 		pgdat->node_id, zone->name, count);
264*61cf5febSJoonsoo Kim }
265*61cf5febSJoonsoo Kim 
266*61cf5febSJoonsoo Kim static void init_zones_in_node(pg_data_t *pgdat)
267*61cf5febSJoonsoo Kim {
268*61cf5febSJoonsoo Kim 	struct zone *zone;
269*61cf5febSJoonsoo Kim 	struct zone *node_zones = pgdat->node_zones;
270*61cf5febSJoonsoo Kim 	unsigned long flags;
271*61cf5febSJoonsoo Kim 
272*61cf5febSJoonsoo Kim 	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
273*61cf5febSJoonsoo Kim 		if (!populated_zone(zone))
274*61cf5febSJoonsoo Kim 			continue;
275*61cf5febSJoonsoo Kim 
276*61cf5febSJoonsoo Kim 		spin_lock_irqsave(&zone->lock, flags);
277*61cf5febSJoonsoo Kim 		init_pages_in_zone(pgdat, zone);
278*61cf5febSJoonsoo Kim 		spin_unlock_irqrestore(&zone->lock, flags);
279*61cf5febSJoonsoo Kim 	}
280*61cf5febSJoonsoo Kim }
281*61cf5febSJoonsoo Kim 
282*61cf5febSJoonsoo Kim static void init_early_allocated_pages(void)
283*61cf5febSJoonsoo Kim {
284*61cf5febSJoonsoo Kim 	pg_data_t *pgdat;
285*61cf5febSJoonsoo Kim 
286*61cf5febSJoonsoo Kim 	drain_all_pages(NULL);
287*61cf5febSJoonsoo Kim 	for_each_online_pgdat(pgdat)
288*61cf5febSJoonsoo Kim 		init_zones_in_node(pgdat);
289*61cf5febSJoonsoo Kim }
290*61cf5febSJoonsoo Kim 
29148c96a36SJoonsoo Kim static const struct file_operations proc_page_owner_operations = {
29248c96a36SJoonsoo Kim 	.read		= read_page_owner,
29348c96a36SJoonsoo Kim };
29448c96a36SJoonsoo Kim 
29548c96a36SJoonsoo Kim static int __init pageowner_init(void)
29648c96a36SJoonsoo Kim {
29748c96a36SJoonsoo Kim 	struct dentry *dentry;
29848c96a36SJoonsoo Kim 
29948c96a36SJoonsoo Kim 	if (!page_owner_inited) {
30048c96a36SJoonsoo Kim 		pr_info("page_owner is disabled\n");
30148c96a36SJoonsoo Kim 		return 0;
30248c96a36SJoonsoo Kim 	}
30348c96a36SJoonsoo Kim 
30448c96a36SJoonsoo Kim 	dentry = debugfs_create_file("page_owner", S_IRUSR, NULL,
30548c96a36SJoonsoo Kim 			NULL, &proc_page_owner_operations);
30648c96a36SJoonsoo Kim 	if (IS_ERR(dentry))
30748c96a36SJoonsoo Kim 		return PTR_ERR(dentry);
30848c96a36SJoonsoo Kim 
30948c96a36SJoonsoo Kim 	return 0;
31048c96a36SJoonsoo Kim }
31148c96a36SJoonsoo Kim module_init(pageowner_init)
312