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> 87dd80b8aSVlastimil Babka #include <linux/jump_label.h> 97cd12b4aSVlastimil Babka #include <linux/migrate.h> 1048c96a36SJoonsoo Kim #include "internal.h" 1148c96a36SJoonsoo Kim 1248c96a36SJoonsoo Kim static bool page_owner_disabled = true; 137dd80b8aSVlastimil Babka DEFINE_STATIC_KEY_FALSE(page_owner_inited); 1448c96a36SJoonsoo Kim 1561cf5febSJoonsoo Kim static void init_early_allocated_pages(void); 1661cf5febSJoonsoo Kim 1748c96a36SJoonsoo Kim static int early_page_owner_param(char *buf) 1848c96a36SJoonsoo Kim { 1948c96a36SJoonsoo Kim if (!buf) 2048c96a36SJoonsoo Kim return -EINVAL; 2148c96a36SJoonsoo Kim 2248c96a36SJoonsoo Kim if (strcmp(buf, "on") == 0) 2348c96a36SJoonsoo Kim page_owner_disabled = false; 2448c96a36SJoonsoo Kim 2548c96a36SJoonsoo Kim return 0; 2648c96a36SJoonsoo Kim } 2748c96a36SJoonsoo Kim early_param("page_owner", early_page_owner_param); 2848c96a36SJoonsoo Kim 2948c96a36SJoonsoo Kim static bool need_page_owner(void) 3048c96a36SJoonsoo Kim { 3148c96a36SJoonsoo Kim if (page_owner_disabled) 3248c96a36SJoonsoo Kim return false; 3348c96a36SJoonsoo Kim 3448c96a36SJoonsoo Kim return true; 3548c96a36SJoonsoo Kim } 3648c96a36SJoonsoo Kim 3748c96a36SJoonsoo Kim static void init_page_owner(void) 3848c96a36SJoonsoo Kim { 3948c96a36SJoonsoo Kim if (page_owner_disabled) 4048c96a36SJoonsoo Kim return; 4148c96a36SJoonsoo Kim 427dd80b8aSVlastimil Babka static_branch_enable(&page_owner_inited); 4361cf5febSJoonsoo Kim init_early_allocated_pages(); 4448c96a36SJoonsoo Kim } 4548c96a36SJoonsoo Kim 4648c96a36SJoonsoo Kim struct page_ext_operations page_owner_ops = { 4748c96a36SJoonsoo Kim .need = need_page_owner, 4848c96a36SJoonsoo Kim .init = init_page_owner, 4948c96a36SJoonsoo Kim }; 5048c96a36SJoonsoo Kim 5148c96a36SJoonsoo Kim void __reset_page_owner(struct page *page, unsigned int order) 5248c96a36SJoonsoo Kim { 5348c96a36SJoonsoo Kim int i; 5448c96a36SJoonsoo Kim struct page_ext *page_ext; 5548c96a36SJoonsoo Kim 5648c96a36SJoonsoo Kim for (i = 0; i < (1 << order); i++) { 5748c96a36SJoonsoo Kim page_ext = lookup_page_ext(page + i); 5848c96a36SJoonsoo Kim __clear_bit(PAGE_EXT_OWNER, &page_ext->flags); 5948c96a36SJoonsoo Kim } 6048c96a36SJoonsoo Kim } 6148c96a36SJoonsoo Kim 6248c96a36SJoonsoo Kim void __set_page_owner(struct page *page, unsigned int order, gfp_t gfp_mask) 6348c96a36SJoonsoo Kim { 6494f759d6SSergei Rogachev struct page_ext *page_ext = lookup_page_ext(page); 6594f759d6SSergei Rogachev struct stack_trace trace = { 6694f759d6SSergei Rogachev .nr_entries = 0, 6794f759d6SSergei Rogachev .max_entries = ARRAY_SIZE(page_ext->trace_entries), 6894f759d6SSergei Rogachev .entries = &page_ext->trace_entries[0], 6994f759d6SSergei Rogachev .skip = 3, 7094f759d6SSergei Rogachev }; 7148c96a36SJoonsoo Kim 7294f759d6SSergei Rogachev save_stack_trace(&trace); 7348c96a36SJoonsoo Kim 7448c96a36SJoonsoo Kim page_ext->order = order; 7548c96a36SJoonsoo Kim page_ext->gfp_mask = gfp_mask; 7694f759d6SSergei Rogachev page_ext->nr_entries = trace.nr_entries; 777cd12b4aSVlastimil Babka page_ext->last_migrate_reason = -1; 7848c96a36SJoonsoo Kim 7948c96a36SJoonsoo Kim __set_bit(PAGE_EXT_OWNER, &page_ext->flags); 8048c96a36SJoonsoo Kim } 8148c96a36SJoonsoo Kim 827cd12b4aSVlastimil Babka void __set_page_owner_migrate_reason(struct page *page, int reason) 837cd12b4aSVlastimil Babka { 847cd12b4aSVlastimil Babka struct page_ext *page_ext = lookup_page_ext(page); 857cd12b4aSVlastimil Babka 867cd12b4aSVlastimil Babka page_ext->last_migrate_reason = reason; 877cd12b4aSVlastimil Babka } 887cd12b4aSVlastimil Babka 89e2cfc911SJoonsoo Kim gfp_t __get_page_owner_gfp(struct page *page) 90e2cfc911SJoonsoo Kim { 91e2cfc911SJoonsoo Kim struct page_ext *page_ext = lookup_page_ext(page); 92e2cfc911SJoonsoo Kim 93e2cfc911SJoonsoo Kim return page_ext->gfp_mask; 94e2cfc911SJoonsoo Kim } 95e2cfc911SJoonsoo Kim 96d435edcaSVlastimil Babka void __copy_page_owner(struct page *oldpage, struct page *newpage) 97d435edcaSVlastimil Babka { 98d435edcaSVlastimil Babka struct page_ext *old_ext = lookup_page_ext(oldpage); 99d435edcaSVlastimil Babka struct page_ext *new_ext = lookup_page_ext(newpage); 100d435edcaSVlastimil Babka int i; 101d435edcaSVlastimil Babka 102d435edcaSVlastimil Babka new_ext->order = old_ext->order; 103d435edcaSVlastimil Babka new_ext->gfp_mask = old_ext->gfp_mask; 104d435edcaSVlastimil Babka new_ext->nr_entries = old_ext->nr_entries; 105d435edcaSVlastimil Babka 106d435edcaSVlastimil Babka for (i = 0; i < ARRAY_SIZE(new_ext->trace_entries); i++) 107d435edcaSVlastimil Babka new_ext->trace_entries[i] = old_ext->trace_entries[i]; 108d435edcaSVlastimil Babka 109d435edcaSVlastimil Babka /* 110d435edcaSVlastimil Babka * We don't clear the bit on the oldpage as it's going to be freed 111d435edcaSVlastimil Babka * after migration. Until then, the info can be useful in case of 112d435edcaSVlastimil Babka * a bug, and the overal stats will be off a bit only temporarily. 113d435edcaSVlastimil Babka * Also, migrate_misplaced_transhuge_page() can still fail the 114d435edcaSVlastimil Babka * migration and then we want the oldpage to retain the info. But 115d435edcaSVlastimil Babka * in that case we also don't need to explicitly clear the info from 116d435edcaSVlastimil Babka * the new page, which will be freed. 117d435edcaSVlastimil Babka */ 118d435edcaSVlastimil Babka __set_bit(PAGE_EXT_OWNER, &new_ext->flags); 119d435edcaSVlastimil Babka } 120d435edcaSVlastimil Babka 12148c96a36SJoonsoo Kim static ssize_t 12248c96a36SJoonsoo Kim print_page_owner(char __user *buf, size_t count, unsigned long pfn, 12348c96a36SJoonsoo Kim struct page *page, struct page_ext *page_ext) 12448c96a36SJoonsoo Kim { 12548c96a36SJoonsoo Kim int ret; 12648c96a36SJoonsoo Kim int pageblock_mt, page_mt; 12748c96a36SJoonsoo Kim char *kbuf; 12894f759d6SSergei Rogachev struct stack_trace trace = { 12994f759d6SSergei Rogachev .nr_entries = page_ext->nr_entries, 13094f759d6SSergei Rogachev .entries = &page_ext->trace_entries[0], 13194f759d6SSergei Rogachev }; 13248c96a36SJoonsoo Kim 13348c96a36SJoonsoo Kim kbuf = kmalloc(count, GFP_KERNEL); 13448c96a36SJoonsoo Kim if (!kbuf) 13548c96a36SJoonsoo Kim return -ENOMEM; 13648c96a36SJoonsoo Kim 13748c96a36SJoonsoo Kim ret = snprintf(kbuf, count, 13860f30350SVlastimil Babka "Page allocated via order %u, mask %#x(%pGg)\n", 13960f30350SVlastimil Babka page_ext->order, page_ext->gfp_mask, 14060f30350SVlastimil Babka &page_ext->gfp_mask); 14148c96a36SJoonsoo Kim 14248c96a36SJoonsoo Kim if (ret >= count) 14348c96a36SJoonsoo Kim goto err; 14448c96a36SJoonsoo Kim 14548c96a36SJoonsoo Kim /* Print information relevant to grouping pages by mobility */ 146*0b423ca2SMel Gorman pageblock_mt = get_pageblock_migratetype(page); 14748c96a36SJoonsoo Kim page_mt = gfpflags_to_migratetype(page_ext->gfp_mask); 14848c96a36SJoonsoo Kim ret += snprintf(kbuf + ret, count - ret, 14960f30350SVlastimil Babka "PFN %lu type %s Block %lu type %s Flags %#lx(%pGp)\n", 15048c96a36SJoonsoo Kim pfn, 15160f30350SVlastimil Babka migratetype_names[page_mt], 15248c96a36SJoonsoo Kim pfn >> pageblock_order, 15360f30350SVlastimil Babka migratetype_names[pageblock_mt], 15460f30350SVlastimil Babka page->flags, &page->flags); 15548c96a36SJoonsoo Kim 15648c96a36SJoonsoo Kim if (ret >= count) 15748c96a36SJoonsoo Kim goto err; 15848c96a36SJoonsoo Kim 15994f759d6SSergei Rogachev ret += snprint_stack_trace(kbuf + ret, count - ret, &trace, 0); 16048c96a36SJoonsoo Kim if (ret >= count) 16148c96a36SJoonsoo Kim goto err; 16248c96a36SJoonsoo Kim 1637cd12b4aSVlastimil Babka if (page_ext->last_migrate_reason != -1) { 1647cd12b4aSVlastimil Babka ret += snprintf(kbuf + ret, count - ret, 1657cd12b4aSVlastimil Babka "Page has been migrated, last migrate reason: %s\n", 1667cd12b4aSVlastimil Babka migrate_reason_names[page_ext->last_migrate_reason]); 1677cd12b4aSVlastimil Babka if (ret >= count) 1687cd12b4aSVlastimil Babka goto err; 1697cd12b4aSVlastimil Babka } 1707cd12b4aSVlastimil Babka 17148c96a36SJoonsoo Kim ret += snprintf(kbuf + ret, count - ret, "\n"); 17248c96a36SJoonsoo Kim if (ret >= count) 17348c96a36SJoonsoo Kim goto err; 17448c96a36SJoonsoo Kim 17548c96a36SJoonsoo Kim if (copy_to_user(buf, kbuf, ret)) 17648c96a36SJoonsoo Kim ret = -EFAULT; 17748c96a36SJoonsoo Kim 17848c96a36SJoonsoo Kim kfree(kbuf); 17948c96a36SJoonsoo Kim return ret; 18048c96a36SJoonsoo Kim 18148c96a36SJoonsoo Kim err: 18248c96a36SJoonsoo Kim kfree(kbuf); 18348c96a36SJoonsoo Kim return -ENOMEM; 18448c96a36SJoonsoo Kim } 18548c96a36SJoonsoo Kim 1864e462112SVlastimil Babka void __dump_page_owner(struct page *page) 1874e462112SVlastimil Babka { 1884e462112SVlastimil Babka struct page_ext *page_ext = lookup_page_ext(page); 1894e462112SVlastimil Babka struct stack_trace trace = { 1904e462112SVlastimil Babka .nr_entries = page_ext->nr_entries, 1914e462112SVlastimil Babka .entries = &page_ext->trace_entries[0], 1924e462112SVlastimil Babka }; 1934e462112SVlastimil Babka gfp_t gfp_mask = page_ext->gfp_mask; 1944e462112SVlastimil Babka int mt = gfpflags_to_migratetype(gfp_mask); 1954e462112SVlastimil Babka 1964e462112SVlastimil Babka if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) { 1974e462112SVlastimil Babka pr_alert("page_owner info is not active (free page?)\n"); 1984e462112SVlastimil Babka return; 1994e462112SVlastimil Babka } 2004e462112SVlastimil Babka 201756a025fSJoe Perches pr_alert("page allocated via order %u, migratetype %s, gfp_mask %#x(%pGg)\n", 202756a025fSJoe Perches page_ext->order, migratetype_names[mt], gfp_mask, &gfp_mask); 2034e462112SVlastimil Babka print_stack_trace(&trace, 0); 2044e462112SVlastimil Babka 2054e462112SVlastimil Babka if (page_ext->last_migrate_reason != -1) 2064e462112SVlastimil Babka pr_alert("page has been migrated, last migrate reason: %s\n", 2074e462112SVlastimil Babka migrate_reason_names[page_ext->last_migrate_reason]); 2084e462112SVlastimil Babka } 2094e462112SVlastimil Babka 21048c96a36SJoonsoo Kim static ssize_t 21148c96a36SJoonsoo Kim read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos) 21248c96a36SJoonsoo Kim { 21348c96a36SJoonsoo Kim unsigned long pfn; 21448c96a36SJoonsoo Kim struct page *page; 21548c96a36SJoonsoo Kim struct page_ext *page_ext; 21648c96a36SJoonsoo Kim 2177dd80b8aSVlastimil Babka if (!static_branch_unlikely(&page_owner_inited)) 21848c96a36SJoonsoo Kim return -EINVAL; 21948c96a36SJoonsoo Kim 22048c96a36SJoonsoo Kim page = NULL; 22148c96a36SJoonsoo Kim pfn = min_low_pfn + *ppos; 22248c96a36SJoonsoo Kim 22348c96a36SJoonsoo Kim /* Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area */ 22448c96a36SJoonsoo Kim while (!pfn_valid(pfn) && (pfn & (MAX_ORDER_NR_PAGES - 1)) != 0) 22548c96a36SJoonsoo Kim pfn++; 22648c96a36SJoonsoo Kim 22748c96a36SJoonsoo Kim drain_all_pages(NULL); 22848c96a36SJoonsoo Kim 22948c96a36SJoonsoo Kim /* Find an allocated page */ 23048c96a36SJoonsoo Kim for (; pfn < max_pfn; pfn++) { 23148c96a36SJoonsoo Kim /* 23248c96a36SJoonsoo Kim * If the new page is in a new MAX_ORDER_NR_PAGES area, 23348c96a36SJoonsoo Kim * validate the area as existing, skip it if not 23448c96a36SJoonsoo Kim */ 23548c96a36SJoonsoo Kim if ((pfn & (MAX_ORDER_NR_PAGES - 1)) == 0 && !pfn_valid(pfn)) { 23648c96a36SJoonsoo Kim pfn += MAX_ORDER_NR_PAGES - 1; 23748c96a36SJoonsoo Kim continue; 23848c96a36SJoonsoo Kim } 23948c96a36SJoonsoo Kim 24048c96a36SJoonsoo Kim /* Check for holes within a MAX_ORDER area */ 24148c96a36SJoonsoo Kim if (!pfn_valid_within(pfn)) 24248c96a36SJoonsoo Kim continue; 24348c96a36SJoonsoo Kim 24448c96a36SJoonsoo Kim page = pfn_to_page(pfn); 24548c96a36SJoonsoo Kim if (PageBuddy(page)) { 24648c96a36SJoonsoo Kim unsigned long freepage_order = page_order_unsafe(page); 24748c96a36SJoonsoo Kim 24848c96a36SJoonsoo Kim if (freepage_order < MAX_ORDER) 24948c96a36SJoonsoo Kim pfn += (1UL << freepage_order) - 1; 25048c96a36SJoonsoo Kim continue; 25148c96a36SJoonsoo Kim } 25248c96a36SJoonsoo Kim 25348c96a36SJoonsoo Kim page_ext = lookup_page_ext(page); 25448c96a36SJoonsoo Kim 25548c96a36SJoonsoo Kim /* 25661cf5febSJoonsoo Kim * Some pages could be missed by concurrent allocation or free, 25761cf5febSJoonsoo Kim * because we don't hold the zone lock. 25848c96a36SJoonsoo Kim */ 25948c96a36SJoonsoo Kim if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) 26048c96a36SJoonsoo Kim continue; 26148c96a36SJoonsoo Kim 26248c96a36SJoonsoo Kim /* Record the next PFN to read in the file offset */ 26348c96a36SJoonsoo Kim *ppos = (pfn - min_low_pfn) + 1; 26448c96a36SJoonsoo Kim 26548c96a36SJoonsoo Kim return print_page_owner(buf, count, pfn, page, page_ext); 26648c96a36SJoonsoo Kim } 26748c96a36SJoonsoo Kim 26848c96a36SJoonsoo Kim return 0; 26948c96a36SJoonsoo Kim } 27048c96a36SJoonsoo Kim 27161cf5febSJoonsoo Kim static void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone) 27261cf5febSJoonsoo Kim { 27361cf5febSJoonsoo Kim struct page *page; 27461cf5febSJoonsoo Kim struct page_ext *page_ext; 27561cf5febSJoonsoo Kim unsigned long pfn = zone->zone_start_pfn, block_end_pfn; 27661cf5febSJoonsoo Kim unsigned long end_pfn = pfn + zone->spanned_pages; 27761cf5febSJoonsoo Kim unsigned long count = 0; 27861cf5febSJoonsoo Kim 27961cf5febSJoonsoo Kim /* Scan block by block. First and last block may be incomplete */ 28061cf5febSJoonsoo Kim pfn = zone->zone_start_pfn; 28161cf5febSJoonsoo Kim 28261cf5febSJoonsoo Kim /* 28361cf5febSJoonsoo Kim * Walk the zone in pageblock_nr_pages steps. If a page block spans 28461cf5febSJoonsoo Kim * a zone boundary, it will be double counted between zones. This does 28561cf5febSJoonsoo Kim * not matter as the mixed block count will still be correct 28661cf5febSJoonsoo Kim */ 28761cf5febSJoonsoo Kim for (; pfn < end_pfn; ) { 28861cf5febSJoonsoo Kim if (!pfn_valid(pfn)) { 28961cf5febSJoonsoo Kim pfn = ALIGN(pfn + 1, MAX_ORDER_NR_PAGES); 29061cf5febSJoonsoo Kim continue; 29161cf5febSJoonsoo Kim } 29261cf5febSJoonsoo Kim 29361cf5febSJoonsoo Kim block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages); 29461cf5febSJoonsoo Kim block_end_pfn = min(block_end_pfn, end_pfn); 29561cf5febSJoonsoo Kim 29661cf5febSJoonsoo Kim page = pfn_to_page(pfn); 29761cf5febSJoonsoo Kim 29861cf5febSJoonsoo Kim for (; pfn < block_end_pfn; pfn++) { 29961cf5febSJoonsoo Kim if (!pfn_valid_within(pfn)) 30061cf5febSJoonsoo Kim continue; 30161cf5febSJoonsoo Kim 30261cf5febSJoonsoo Kim page = pfn_to_page(pfn); 30361cf5febSJoonsoo Kim 3049d43f5aeSJoonsoo Kim if (page_zone(page) != zone) 3059d43f5aeSJoonsoo Kim continue; 3069d43f5aeSJoonsoo Kim 30761cf5febSJoonsoo Kim /* 30861cf5febSJoonsoo Kim * We are safe to check buddy flag and order, because 30961cf5febSJoonsoo Kim * this is init stage and only single thread runs. 31061cf5febSJoonsoo Kim */ 31161cf5febSJoonsoo Kim if (PageBuddy(page)) { 31261cf5febSJoonsoo Kim pfn += (1UL << page_order(page)) - 1; 31361cf5febSJoonsoo Kim continue; 31461cf5febSJoonsoo Kim } 31561cf5febSJoonsoo Kim 31661cf5febSJoonsoo Kim if (PageReserved(page)) 31761cf5febSJoonsoo Kim continue; 31861cf5febSJoonsoo Kim 31961cf5febSJoonsoo Kim page_ext = lookup_page_ext(page); 32061cf5febSJoonsoo Kim 32161cf5febSJoonsoo Kim /* Maybe overraping zone */ 32261cf5febSJoonsoo Kim if (test_bit(PAGE_EXT_OWNER, &page_ext->flags)) 32361cf5febSJoonsoo Kim continue; 32461cf5febSJoonsoo Kim 32561cf5febSJoonsoo Kim /* Found early allocated page */ 32661cf5febSJoonsoo Kim set_page_owner(page, 0, 0); 32761cf5febSJoonsoo Kim count++; 32861cf5febSJoonsoo Kim } 32961cf5febSJoonsoo Kim } 33061cf5febSJoonsoo Kim 33161cf5febSJoonsoo Kim pr_info("Node %d, zone %8s: page owner found early allocated %lu pages\n", 33261cf5febSJoonsoo Kim pgdat->node_id, zone->name, count); 33361cf5febSJoonsoo Kim } 33461cf5febSJoonsoo Kim 33561cf5febSJoonsoo Kim static void init_zones_in_node(pg_data_t *pgdat) 33661cf5febSJoonsoo Kim { 33761cf5febSJoonsoo Kim struct zone *zone; 33861cf5febSJoonsoo Kim struct zone *node_zones = pgdat->node_zones; 33961cf5febSJoonsoo Kim unsigned long flags; 34061cf5febSJoonsoo Kim 34161cf5febSJoonsoo Kim for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { 34261cf5febSJoonsoo Kim if (!populated_zone(zone)) 34361cf5febSJoonsoo Kim continue; 34461cf5febSJoonsoo Kim 34561cf5febSJoonsoo Kim spin_lock_irqsave(&zone->lock, flags); 34661cf5febSJoonsoo Kim init_pages_in_zone(pgdat, zone); 34761cf5febSJoonsoo Kim spin_unlock_irqrestore(&zone->lock, flags); 34861cf5febSJoonsoo Kim } 34961cf5febSJoonsoo Kim } 35061cf5febSJoonsoo Kim 35161cf5febSJoonsoo Kim static void init_early_allocated_pages(void) 35261cf5febSJoonsoo Kim { 35361cf5febSJoonsoo Kim pg_data_t *pgdat; 35461cf5febSJoonsoo Kim 35561cf5febSJoonsoo Kim drain_all_pages(NULL); 35661cf5febSJoonsoo Kim for_each_online_pgdat(pgdat) 35761cf5febSJoonsoo Kim init_zones_in_node(pgdat); 35861cf5febSJoonsoo Kim } 35961cf5febSJoonsoo Kim 36048c96a36SJoonsoo Kim static const struct file_operations proc_page_owner_operations = { 36148c96a36SJoonsoo Kim .read = read_page_owner, 36248c96a36SJoonsoo Kim }; 36348c96a36SJoonsoo Kim 36448c96a36SJoonsoo Kim static int __init pageowner_init(void) 36548c96a36SJoonsoo Kim { 36648c96a36SJoonsoo Kim struct dentry *dentry; 36748c96a36SJoonsoo Kim 3687dd80b8aSVlastimil Babka if (!static_branch_unlikely(&page_owner_inited)) { 36948c96a36SJoonsoo Kim pr_info("page_owner is disabled\n"); 37048c96a36SJoonsoo Kim return 0; 37148c96a36SJoonsoo Kim } 37248c96a36SJoonsoo Kim 37348c96a36SJoonsoo Kim dentry = debugfs_create_file("page_owner", S_IRUSR, NULL, 37448c96a36SJoonsoo Kim NULL, &proc_page_owner_operations); 37548c96a36SJoonsoo Kim if (IS_ERR(dentry)) 37648c96a36SJoonsoo Kim return PTR_ERR(dentry); 37748c96a36SJoonsoo Kim 37848c96a36SJoonsoo Kim return 0; 37948c96a36SJoonsoo Kim } 38044c5af96SPaul Gortmaker late_initcall(pageowner_init) 381