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