162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/debugfs.h> 362306a36Sopenharmony_ci#include <linux/mm.h> 462306a36Sopenharmony_ci#include <linux/slab.h> 562306a36Sopenharmony_ci#include <linux/uaccess.h> 662306a36Sopenharmony_ci#include <linux/memblock.h> 762306a36Sopenharmony_ci#include <linux/stacktrace.h> 862306a36Sopenharmony_ci#include <linux/page_owner.h> 962306a36Sopenharmony_ci#include <linux/jump_label.h> 1062306a36Sopenharmony_ci#include <linux/migrate.h> 1162306a36Sopenharmony_ci#include <linux/stackdepot.h> 1262306a36Sopenharmony_ci#include <linux/seq_file.h> 1362306a36Sopenharmony_ci#include <linux/memcontrol.h> 1462306a36Sopenharmony_ci#include <linux/sched/clock.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "internal.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * TODO: teach PAGE_OWNER_STACK_DEPTH (__dump_page_owner and save_stack) 2062306a36Sopenharmony_ci * to use off stack temporal storage 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci#define PAGE_OWNER_STACK_DEPTH (16) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct page_owner { 2562306a36Sopenharmony_ci unsigned short order; 2662306a36Sopenharmony_ci short last_migrate_reason; 2762306a36Sopenharmony_ci gfp_t gfp_mask; 2862306a36Sopenharmony_ci depot_stack_handle_t handle; 2962306a36Sopenharmony_ci depot_stack_handle_t free_handle; 3062306a36Sopenharmony_ci u64 ts_nsec; 3162306a36Sopenharmony_ci u64 free_ts_nsec; 3262306a36Sopenharmony_ci char comm[TASK_COMM_LEN]; 3362306a36Sopenharmony_ci pid_t pid; 3462306a36Sopenharmony_ci pid_t tgid; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic bool page_owner_enabled __initdata; 3862306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(page_owner_inited); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic depot_stack_handle_t dummy_handle; 4162306a36Sopenharmony_cistatic depot_stack_handle_t failure_handle; 4262306a36Sopenharmony_cistatic depot_stack_handle_t early_handle; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void init_early_allocated_pages(void); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int __init early_page_owner_param(char *buf) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int ret = kstrtobool(buf, &page_owner_enabled); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (page_owner_enabled) 5162306a36Sopenharmony_ci stack_depot_request_early_init(); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return ret; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ciearly_param("page_owner", early_page_owner_param); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic __init bool need_page_owner(void) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return page_owner_enabled; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic __always_inline depot_stack_handle_t create_dummy_stack(void) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci unsigned long entries[4]; 6562306a36Sopenharmony_ci unsigned int nr_entries; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 0); 6862306a36Sopenharmony_ci return stack_depot_save(entries, nr_entries, GFP_KERNEL); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic noinline void register_dummy_stack(void) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci dummy_handle = create_dummy_stack(); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic noinline void register_failure_stack(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci failure_handle = create_dummy_stack(); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic noinline void register_early_stack(void) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci early_handle = create_dummy_stack(); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic __init void init_page_owner(void) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci if (!page_owner_enabled) 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci register_dummy_stack(); 9262306a36Sopenharmony_ci register_failure_stack(); 9362306a36Sopenharmony_ci register_early_stack(); 9462306a36Sopenharmony_ci static_branch_enable(&page_owner_inited); 9562306a36Sopenharmony_ci init_early_allocated_pages(); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct page_ext_operations page_owner_ops = { 9962306a36Sopenharmony_ci .size = sizeof(struct page_owner), 10062306a36Sopenharmony_ci .need = need_page_owner, 10162306a36Sopenharmony_ci .init = init_page_owner, 10262306a36Sopenharmony_ci .need_shared_flags = true, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic inline struct page_owner *get_page_owner(struct page_ext *page_ext) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci return page_ext_data(page_ext, &page_owner_ops); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic noinline depot_stack_handle_t save_stack(gfp_t flags) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci unsigned long entries[PAGE_OWNER_STACK_DEPTH]; 11362306a36Sopenharmony_ci depot_stack_handle_t handle; 11462306a36Sopenharmony_ci unsigned int nr_entries; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * Avoid recursion. 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Sometimes page metadata allocation tracking requires more 12062306a36Sopenharmony_ci * memory to be allocated: 12162306a36Sopenharmony_ci * - when new stack trace is saved to stack depot 12262306a36Sopenharmony_ci * - when backtrace itself is calculated (ia64) 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci if (current->in_page_owner) 12562306a36Sopenharmony_ci return dummy_handle; 12662306a36Sopenharmony_ci current->in_page_owner = 1; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 2); 12962306a36Sopenharmony_ci handle = stack_depot_save(entries, nr_entries, flags); 13062306a36Sopenharmony_ci if (!handle) 13162306a36Sopenharmony_ci handle = failure_handle; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci current->in_page_owner = 0; 13462306a36Sopenharmony_ci return handle; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_civoid __reset_page_owner(struct page *page, unsigned short order) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int i; 14062306a36Sopenharmony_ci struct page_ext *page_ext; 14162306a36Sopenharmony_ci depot_stack_handle_t handle; 14262306a36Sopenharmony_ci struct page_owner *page_owner; 14362306a36Sopenharmony_ci u64 free_ts_nsec = local_clock(); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci page_ext = page_ext_get(page); 14662306a36Sopenharmony_ci if (unlikely(!page_ext)) 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci handle = save_stack(GFP_NOWAIT | __GFP_NOWARN); 15062306a36Sopenharmony_ci for (i = 0; i < (1 << order); i++) { 15162306a36Sopenharmony_ci __clear_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags); 15262306a36Sopenharmony_ci page_owner = get_page_owner(page_ext); 15362306a36Sopenharmony_ci page_owner->free_handle = handle; 15462306a36Sopenharmony_ci page_owner->free_ts_nsec = free_ts_nsec; 15562306a36Sopenharmony_ci page_ext = page_ext_next(page_ext); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci page_ext_put(page_ext); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline void __set_page_owner_handle(struct page_ext *page_ext, 16162306a36Sopenharmony_ci depot_stack_handle_t handle, 16262306a36Sopenharmony_ci unsigned short order, gfp_t gfp_mask) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct page_owner *page_owner; 16562306a36Sopenharmony_ci int i; 16662306a36Sopenharmony_ci u64 ts_nsec = local_clock(); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci for (i = 0; i < (1 << order); i++) { 16962306a36Sopenharmony_ci page_owner = get_page_owner(page_ext); 17062306a36Sopenharmony_ci page_owner->handle = handle; 17162306a36Sopenharmony_ci page_owner->order = order; 17262306a36Sopenharmony_ci page_owner->gfp_mask = gfp_mask; 17362306a36Sopenharmony_ci page_owner->last_migrate_reason = -1; 17462306a36Sopenharmony_ci page_owner->pid = current->pid; 17562306a36Sopenharmony_ci page_owner->tgid = current->tgid; 17662306a36Sopenharmony_ci page_owner->ts_nsec = ts_nsec; 17762306a36Sopenharmony_ci strscpy(page_owner->comm, current->comm, 17862306a36Sopenharmony_ci sizeof(page_owner->comm)); 17962306a36Sopenharmony_ci __set_bit(PAGE_EXT_OWNER, &page_ext->flags); 18062306a36Sopenharmony_ci __set_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci page_ext = page_ext_next(page_ext); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cinoinline void __set_page_owner(struct page *page, unsigned short order, 18762306a36Sopenharmony_ci gfp_t gfp_mask) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct page_ext *page_ext; 19062306a36Sopenharmony_ci depot_stack_handle_t handle; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci handle = save_stack(gfp_mask); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci page_ext = page_ext_get(page); 19562306a36Sopenharmony_ci if (unlikely(!page_ext)) 19662306a36Sopenharmony_ci return; 19762306a36Sopenharmony_ci __set_page_owner_handle(page_ext, handle, order, gfp_mask); 19862306a36Sopenharmony_ci page_ext_put(page_ext); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_civoid __set_page_owner_migrate_reason(struct page *page, int reason) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct page_ext *page_ext = page_ext_get(page); 20462306a36Sopenharmony_ci struct page_owner *page_owner; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (unlikely(!page_ext)) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci page_owner = get_page_owner(page_ext); 21062306a36Sopenharmony_ci page_owner->last_migrate_reason = reason; 21162306a36Sopenharmony_ci page_ext_put(page_ext); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_civoid __split_page_owner(struct page *page, unsigned int nr) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci int i; 21762306a36Sopenharmony_ci struct page_ext *page_ext = page_ext_get(page); 21862306a36Sopenharmony_ci struct page_owner *page_owner; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (unlikely(!page_ext)) 22162306a36Sopenharmony_ci return; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci for (i = 0; i < nr; i++) { 22462306a36Sopenharmony_ci page_owner = get_page_owner(page_ext); 22562306a36Sopenharmony_ci page_owner->order = 0; 22662306a36Sopenharmony_ci page_ext = page_ext_next(page_ext); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci page_ext_put(page_ext); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_civoid __folio_copy_owner(struct folio *newfolio, struct folio *old) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct page_ext *old_ext; 23462306a36Sopenharmony_ci struct page_ext *new_ext; 23562306a36Sopenharmony_ci struct page_owner *old_page_owner, *new_page_owner; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci old_ext = page_ext_get(&old->page); 23862306a36Sopenharmony_ci if (unlikely(!old_ext)) 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci new_ext = page_ext_get(&newfolio->page); 24262306a36Sopenharmony_ci if (unlikely(!new_ext)) { 24362306a36Sopenharmony_ci page_ext_put(old_ext); 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci old_page_owner = get_page_owner(old_ext); 24862306a36Sopenharmony_ci new_page_owner = get_page_owner(new_ext); 24962306a36Sopenharmony_ci new_page_owner->order = old_page_owner->order; 25062306a36Sopenharmony_ci new_page_owner->gfp_mask = old_page_owner->gfp_mask; 25162306a36Sopenharmony_ci new_page_owner->last_migrate_reason = 25262306a36Sopenharmony_ci old_page_owner->last_migrate_reason; 25362306a36Sopenharmony_ci new_page_owner->handle = old_page_owner->handle; 25462306a36Sopenharmony_ci new_page_owner->pid = old_page_owner->pid; 25562306a36Sopenharmony_ci new_page_owner->tgid = old_page_owner->tgid; 25662306a36Sopenharmony_ci new_page_owner->ts_nsec = old_page_owner->ts_nsec; 25762306a36Sopenharmony_ci new_page_owner->free_ts_nsec = old_page_owner->ts_nsec; 25862306a36Sopenharmony_ci strcpy(new_page_owner->comm, old_page_owner->comm); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * We don't clear the bit on the old folio as it's going to be freed 26262306a36Sopenharmony_ci * after migration. Until then, the info can be useful in case of 26362306a36Sopenharmony_ci * a bug, and the overall stats will be off a bit only temporarily. 26462306a36Sopenharmony_ci * Also, migrate_misplaced_transhuge_page() can still fail the 26562306a36Sopenharmony_ci * migration and then we want the old folio to retain the info. But 26662306a36Sopenharmony_ci * in that case we also don't need to explicitly clear the info from 26762306a36Sopenharmony_ci * the new page, which will be freed. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci __set_bit(PAGE_EXT_OWNER, &new_ext->flags); 27062306a36Sopenharmony_ci __set_bit(PAGE_EXT_OWNER_ALLOCATED, &new_ext->flags); 27162306a36Sopenharmony_ci page_ext_put(new_ext); 27262306a36Sopenharmony_ci page_ext_put(old_ext); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_civoid pagetypeinfo_showmixedcount_print(struct seq_file *m, 27662306a36Sopenharmony_ci pg_data_t *pgdat, struct zone *zone) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct page *page; 27962306a36Sopenharmony_ci struct page_ext *page_ext; 28062306a36Sopenharmony_ci struct page_owner *page_owner; 28162306a36Sopenharmony_ci unsigned long pfn, block_end_pfn; 28262306a36Sopenharmony_ci unsigned long end_pfn = zone_end_pfn(zone); 28362306a36Sopenharmony_ci unsigned long count[MIGRATE_TYPES] = { 0, }; 28462306a36Sopenharmony_ci int pageblock_mt, page_mt; 28562306a36Sopenharmony_ci int i; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Scan block by block. First and last block may be incomplete */ 28862306a36Sopenharmony_ci pfn = zone->zone_start_pfn; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Walk the zone in pageblock_nr_pages steps. If a page block spans 29262306a36Sopenharmony_ci * a zone boundary, it will be double counted between zones. This does 29362306a36Sopenharmony_ci * not matter as the mixed block count will still be correct 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci for (; pfn < end_pfn; ) { 29662306a36Sopenharmony_ci page = pfn_to_online_page(pfn); 29762306a36Sopenharmony_ci if (!page) { 29862306a36Sopenharmony_ci pfn = ALIGN(pfn + 1, MAX_ORDER_NR_PAGES); 29962306a36Sopenharmony_ci continue; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci block_end_pfn = pageblock_end_pfn(pfn); 30362306a36Sopenharmony_ci block_end_pfn = min(block_end_pfn, end_pfn); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci pageblock_mt = get_pageblock_migratetype(page); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci for (; pfn < block_end_pfn; pfn++) { 30862306a36Sopenharmony_ci /* The pageblock is online, no need to recheck. */ 30962306a36Sopenharmony_ci page = pfn_to_page(pfn); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (page_zone(page) != zone) 31262306a36Sopenharmony_ci continue; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (PageBuddy(page)) { 31562306a36Sopenharmony_ci unsigned long freepage_order; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci freepage_order = buddy_order_unsafe(page); 31862306a36Sopenharmony_ci if (freepage_order <= MAX_ORDER) 31962306a36Sopenharmony_ci pfn += (1UL << freepage_order) - 1; 32062306a36Sopenharmony_ci continue; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (PageReserved(page)) 32462306a36Sopenharmony_ci continue; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci page_ext = page_ext_get(page); 32762306a36Sopenharmony_ci if (unlikely(!page_ext)) 32862306a36Sopenharmony_ci continue; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!test_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags)) 33162306a36Sopenharmony_ci goto ext_put_continue; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci page_owner = get_page_owner(page_ext); 33462306a36Sopenharmony_ci page_mt = gfp_migratetype(page_owner->gfp_mask); 33562306a36Sopenharmony_ci if (pageblock_mt != page_mt) { 33662306a36Sopenharmony_ci if (is_migrate_cma(pageblock_mt)) 33762306a36Sopenharmony_ci count[MIGRATE_MOVABLE]++; 33862306a36Sopenharmony_ci else 33962306a36Sopenharmony_ci count[pageblock_mt]++; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci pfn = block_end_pfn; 34262306a36Sopenharmony_ci page_ext_put(page_ext); 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci pfn += (1UL << page_owner->order) - 1; 34662306a36Sopenharmony_ciext_put_continue: 34762306a36Sopenharmony_ci page_ext_put(page_ext); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Print counts */ 35262306a36Sopenharmony_ci seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name); 35362306a36Sopenharmony_ci for (i = 0; i < MIGRATE_TYPES; i++) 35462306a36Sopenharmony_ci seq_printf(m, "%12lu ", count[i]); 35562306a36Sopenharmony_ci seq_putc(m, '\n'); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci/* 35962306a36Sopenharmony_ci * Looking for memcg information and print it out 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_cistatic inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, 36262306a36Sopenharmony_ci struct page *page) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci#ifdef CONFIG_MEMCG 36562306a36Sopenharmony_ci unsigned long memcg_data; 36662306a36Sopenharmony_ci struct mem_cgroup *memcg; 36762306a36Sopenharmony_ci bool online; 36862306a36Sopenharmony_ci char name[80]; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci rcu_read_lock(); 37162306a36Sopenharmony_ci memcg_data = READ_ONCE(page->memcg_data); 37262306a36Sopenharmony_ci if (!memcg_data) 37362306a36Sopenharmony_ci goto out_unlock; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (memcg_data & MEMCG_DATA_OBJCGS) 37662306a36Sopenharmony_ci ret += scnprintf(kbuf + ret, count - ret, 37762306a36Sopenharmony_ci "Slab cache page\n"); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci memcg = page_memcg_check(page); 38062306a36Sopenharmony_ci if (!memcg) 38162306a36Sopenharmony_ci goto out_unlock; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci online = (memcg->css.flags & CSS_ONLINE); 38462306a36Sopenharmony_ci cgroup_name(memcg->css.cgroup, name, sizeof(name)); 38562306a36Sopenharmony_ci ret += scnprintf(kbuf + ret, count - ret, 38662306a36Sopenharmony_ci "Charged %sto %smemcg %s\n", 38762306a36Sopenharmony_ci PageMemcgKmem(page) ? "(via objcg) " : "", 38862306a36Sopenharmony_ci online ? "" : "offline ", 38962306a36Sopenharmony_ci name); 39062306a36Sopenharmony_ciout_unlock: 39162306a36Sopenharmony_ci rcu_read_unlock(); 39262306a36Sopenharmony_ci#endif /* CONFIG_MEMCG */ 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return ret; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic ssize_t 39862306a36Sopenharmony_ciprint_page_owner(char __user *buf, size_t count, unsigned long pfn, 39962306a36Sopenharmony_ci struct page *page, struct page_owner *page_owner, 40062306a36Sopenharmony_ci depot_stack_handle_t handle) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int ret, pageblock_mt, page_mt; 40362306a36Sopenharmony_ci char *kbuf; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci count = min_t(size_t, count, PAGE_SIZE); 40662306a36Sopenharmony_ci kbuf = kmalloc(count, GFP_KERNEL); 40762306a36Sopenharmony_ci if (!kbuf) 40862306a36Sopenharmony_ci return -ENOMEM; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = scnprintf(kbuf, count, 41162306a36Sopenharmony_ci "Page allocated via order %u, mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu ns, free_ts %llu ns\n", 41262306a36Sopenharmony_ci page_owner->order, page_owner->gfp_mask, 41362306a36Sopenharmony_ci &page_owner->gfp_mask, page_owner->pid, 41462306a36Sopenharmony_ci page_owner->tgid, page_owner->comm, 41562306a36Sopenharmony_ci page_owner->ts_nsec, page_owner->free_ts_nsec); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Print information relevant to grouping pages by mobility */ 41862306a36Sopenharmony_ci pageblock_mt = get_pageblock_migratetype(page); 41962306a36Sopenharmony_ci page_mt = gfp_migratetype(page_owner->gfp_mask); 42062306a36Sopenharmony_ci ret += scnprintf(kbuf + ret, count - ret, 42162306a36Sopenharmony_ci "PFN 0x%lx type %s Block %lu type %s Flags %pGp\n", 42262306a36Sopenharmony_ci pfn, 42362306a36Sopenharmony_ci migratetype_names[page_mt], 42462306a36Sopenharmony_ci pfn >> pageblock_order, 42562306a36Sopenharmony_ci migratetype_names[pageblock_mt], 42662306a36Sopenharmony_ci &page->flags); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret += stack_depot_snprint(handle, kbuf + ret, count - ret, 0); 42962306a36Sopenharmony_ci if (ret >= count) 43062306a36Sopenharmony_ci goto err; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (page_owner->last_migrate_reason != -1) { 43362306a36Sopenharmony_ci ret += scnprintf(kbuf + ret, count - ret, 43462306a36Sopenharmony_ci "Page has been migrated, last migrate reason: %s\n", 43562306a36Sopenharmony_ci migrate_reason_names[page_owner->last_migrate_reason]); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci ret = print_page_owner_memcg(kbuf, count, ret, page); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ret += snprintf(kbuf + ret, count - ret, "\n"); 44162306a36Sopenharmony_ci if (ret >= count) 44262306a36Sopenharmony_ci goto err; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (copy_to_user(buf, kbuf, ret)) 44562306a36Sopenharmony_ci ret = -EFAULT; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci kfree(kbuf); 44862306a36Sopenharmony_ci return ret; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cierr: 45162306a36Sopenharmony_ci kfree(kbuf); 45262306a36Sopenharmony_ci return -ENOMEM; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_civoid __dump_page_owner(const struct page *page) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct page_ext *page_ext = page_ext_get((void *)page); 45862306a36Sopenharmony_ci struct page_owner *page_owner; 45962306a36Sopenharmony_ci depot_stack_handle_t handle; 46062306a36Sopenharmony_ci gfp_t gfp_mask; 46162306a36Sopenharmony_ci int mt; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (unlikely(!page_ext)) { 46462306a36Sopenharmony_ci pr_alert("There is not page extension available.\n"); 46562306a36Sopenharmony_ci return; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci page_owner = get_page_owner(page_ext); 46962306a36Sopenharmony_ci gfp_mask = page_owner->gfp_mask; 47062306a36Sopenharmony_ci mt = gfp_migratetype(gfp_mask); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) { 47362306a36Sopenharmony_ci pr_alert("page_owner info is not present (never set?)\n"); 47462306a36Sopenharmony_ci page_ext_put(page_ext); 47562306a36Sopenharmony_ci return; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (test_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags)) 47962306a36Sopenharmony_ci pr_alert("page_owner tracks the page as allocated\n"); 48062306a36Sopenharmony_ci else 48162306a36Sopenharmony_ci pr_alert("page_owner tracks the page as freed\n"); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci pr_alert("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu, free_ts %llu\n", 48462306a36Sopenharmony_ci page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask, 48562306a36Sopenharmony_ci page_owner->pid, page_owner->tgid, page_owner->comm, 48662306a36Sopenharmony_ci page_owner->ts_nsec, page_owner->free_ts_nsec); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci handle = READ_ONCE(page_owner->handle); 48962306a36Sopenharmony_ci if (!handle) 49062306a36Sopenharmony_ci pr_alert("page_owner allocation stack trace missing\n"); 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci stack_depot_print(handle); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci handle = READ_ONCE(page_owner->free_handle); 49562306a36Sopenharmony_ci if (!handle) { 49662306a36Sopenharmony_ci pr_alert("page_owner free stack trace missing\n"); 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci pr_alert("page last free stack trace:\n"); 49962306a36Sopenharmony_ci stack_depot_print(handle); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (page_owner->last_migrate_reason != -1) 50362306a36Sopenharmony_ci pr_alert("page has been migrated, last migrate reason: %s\n", 50462306a36Sopenharmony_ci migrate_reason_names[page_owner->last_migrate_reason]); 50562306a36Sopenharmony_ci page_ext_put(page_ext); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic ssize_t 50962306a36Sopenharmony_ciread_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci unsigned long pfn; 51262306a36Sopenharmony_ci struct page *page; 51362306a36Sopenharmony_ci struct page_ext *page_ext; 51462306a36Sopenharmony_ci struct page_owner *page_owner; 51562306a36Sopenharmony_ci depot_stack_handle_t handle; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!static_branch_unlikely(&page_owner_inited)) 51862306a36Sopenharmony_ci return -EINVAL; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci page = NULL; 52162306a36Sopenharmony_ci if (*ppos == 0) 52262306a36Sopenharmony_ci pfn = min_low_pfn; 52362306a36Sopenharmony_ci else 52462306a36Sopenharmony_ci pfn = *ppos; 52562306a36Sopenharmony_ci /* Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area */ 52662306a36Sopenharmony_ci while (!pfn_valid(pfn) && (pfn & (MAX_ORDER_NR_PAGES - 1)) != 0) 52762306a36Sopenharmony_ci pfn++; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Find an allocated page */ 53062306a36Sopenharmony_ci for (; pfn < max_pfn; pfn++) { 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * This temporary page_owner is required so 53362306a36Sopenharmony_ci * that we can avoid the context switches while holding 53462306a36Sopenharmony_ci * the rcu lock and copying the page owner information to 53562306a36Sopenharmony_ci * user through copy_to_user() or GFP_KERNEL allocations. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci struct page_owner page_owner_tmp; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * If the new page is in a new MAX_ORDER_NR_PAGES area, 54162306a36Sopenharmony_ci * validate the area as existing, skip it if not 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ci if ((pfn & (MAX_ORDER_NR_PAGES - 1)) == 0 && !pfn_valid(pfn)) { 54462306a36Sopenharmony_ci pfn += MAX_ORDER_NR_PAGES - 1; 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci page = pfn_to_page(pfn); 54962306a36Sopenharmony_ci if (PageBuddy(page)) { 55062306a36Sopenharmony_ci unsigned long freepage_order = buddy_order_unsafe(page); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (freepage_order <= MAX_ORDER) 55362306a36Sopenharmony_ci pfn += (1UL << freepage_order) - 1; 55462306a36Sopenharmony_ci continue; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci page_ext = page_ext_get(page); 55862306a36Sopenharmony_ci if (unlikely(!page_ext)) 55962306a36Sopenharmony_ci continue; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * Some pages could be missed by concurrent allocation or free, 56362306a36Sopenharmony_ci * because we don't hold the zone lock. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags)) 56662306a36Sopenharmony_ci goto ext_put_continue; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* 56962306a36Sopenharmony_ci * Although we do have the info about past allocation of free 57062306a36Sopenharmony_ci * pages, it's not relevant for current memory usage. 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci if (!test_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags)) 57362306a36Sopenharmony_ci goto ext_put_continue; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci page_owner = get_page_owner(page_ext); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* 57862306a36Sopenharmony_ci * Don't print "tail" pages of high-order allocations as that 57962306a36Sopenharmony_ci * would inflate the stats. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci if (!IS_ALIGNED(pfn, 1 << page_owner->order)) 58262306a36Sopenharmony_ci goto ext_put_continue; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* 58562306a36Sopenharmony_ci * Access to page_ext->handle isn't synchronous so we should 58662306a36Sopenharmony_ci * be careful to access it. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci handle = READ_ONCE(page_owner->handle); 58962306a36Sopenharmony_ci if (!handle) 59062306a36Sopenharmony_ci goto ext_put_continue; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Record the next PFN to read in the file offset */ 59362306a36Sopenharmony_ci *ppos = pfn + 1; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci page_owner_tmp = *page_owner; 59662306a36Sopenharmony_ci page_ext_put(page_ext); 59762306a36Sopenharmony_ci return print_page_owner(buf, count, pfn, page, 59862306a36Sopenharmony_ci &page_owner_tmp, handle); 59962306a36Sopenharmony_ciext_put_continue: 60062306a36Sopenharmony_ci page_ext_put(page_ext); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic loff_t lseek_page_owner(struct file *file, loff_t offset, int orig) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci switch (orig) { 60962306a36Sopenharmony_ci case SEEK_SET: 61062306a36Sopenharmony_ci file->f_pos = offset; 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci case SEEK_CUR: 61362306a36Sopenharmony_ci file->f_pos += offset; 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci default: 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci return file->f_pos; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci unsigned long pfn = zone->zone_start_pfn; 62462306a36Sopenharmony_ci unsigned long end_pfn = zone_end_pfn(zone); 62562306a36Sopenharmony_ci unsigned long count = 0; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * Walk the zone in pageblock_nr_pages steps. If a page block spans 62962306a36Sopenharmony_ci * a zone boundary, it will be double counted between zones. This does 63062306a36Sopenharmony_ci * not matter as the mixed block count will still be correct 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci for (; pfn < end_pfn; ) { 63362306a36Sopenharmony_ci unsigned long block_end_pfn; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!pfn_valid(pfn)) { 63662306a36Sopenharmony_ci pfn = ALIGN(pfn + 1, MAX_ORDER_NR_PAGES); 63762306a36Sopenharmony_ci continue; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci block_end_pfn = pageblock_end_pfn(pfn); 64162306a36Sopenharmony_ci block_end_pfn = min(block_end_pfn, end_pfn); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci for (; pfn < block_end_pfn; pfn++) { 64462306a36Sopenharmony_ci struct page *page = pfn_to_page(pfn); 64562306a36Sopenharmony_ci struct page_ext *page_ext; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (page_zone(page) != zone) 64862306a36Sopenharmony_ci continue; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* 65162306a36Sopenharmony_ci * To avoid having to grab zone->lock, be a little 65262306a36Sopenharmony_ci * careful when reading buddy page order. The only 65362306a36Sopenharmony_ci * danger is that we skip too much and potentially miss 65462306a36Sopenharmony_ci * some early allocated pages, which is better than 65562306a36Sopenharmony_ci * heavy lock contention. 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_ci if (PageBuddy(page)) { 65862306a36Sopenharmony_ci unsigned long order = buddy_order_unsafe(page); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (order > 0 && order <= MAX_ORDER) 66162306a36Sopenharmony_ci pfn += (1UL << order) - 1; 66262306a36Sopenharmony_ci continue; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (PageReserved(page)) 66662306a36Sopenharmony_ci continue; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci page_ext = page_ext_get(page); 66962306a36Sopenharmony_ci if (unlikely(!page_ext)) 67062306a36Sopenharmony_ci continue; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Maybe overlapping zone */ 67362306a36Sopenharmony_ci if (test_bit(PAGE_EXT_OWNER, &page_ext->flags)) 67462306a36Sopenharmony_ci goto ext_put_continue; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Found early allocated page */ 67762306a36Sopenharmony_ci __set_page_owner_handle(page_ext, early_handle, 67862306a36Sopenharmony_ci 0, 0); 67962306a36Sopenharmony_ci count++; 68062306a36Sopenharmony_ciext_put_continue: 68162306a36Sopenharmony_ci page_ext_put(page_ext); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci cond_resched(); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci pr_info("Node %d, zone %8s: page owner found early allocated %lu pages\n", 68762306a36Sopenharmony_ci pgdat->node_id, zone->name, count); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic void init_zones_in_node(pg_data_t *pgdat) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct zone *zone; 69362306a36Sopenharmony_ci struct zone *node_zones = pgdat->node_zones; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { 69662306a36Sopenharmony_ci if (!populated_zone(zone)) 69762306a36Sopenharmony_ci continue; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci init_pages_in_zone(pgdat, zone); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic void init_early_allocated_pages(void) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci pg_data_t *pgdat; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci for_each_online_pgdat(pgdat) 70862306a36Sopenharmony_ci init_zones_in_node(pgdat); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic const struct file_operations proc_page_owner_operations = { 71262306a36Sopenharmony_ci .read = read_page_owner, 71362306a36Sopenharmony_ci .llseek = lseek_page_owner, 71462306a36Sopenharmony_ci}; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int __init pageowner_init(void) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci if (!static_branch_unlikely(&page_owner_inited)) { 71962306a36Sopenharmony_ci pr_info("page_owner is disabled\n"); 72062306a36Sopenharmony_ci return 0; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci debugfs_create_file("page_owner", 0400, NULL, NULL, 72462306a36Sopenharmony_ci &proc_page_owner_operations); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_cilate_initcall(pageowner_init) 729