162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * mm/debug.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * mm/ specific debug routines.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/mm.h>
1162306a36Sopenharmony_ci#include <linux/trace_events.h>
1262306a36Sopenharmony_ci#include <linux/memcontrol.h>
1362306a36Sopenharmony_ci#include <trace/events/mmflags.h>
1462306a36Sopenharmony_ci#include <linux/migrate.h>
1562306a36Sopenharmony_ci#include <linux/page_owner.h>
1662306a36Sopenharmony_ci#include <linux/ctype.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "internal.h"
1962306a36Sopenharmony_ci#include <trace/events/migrate.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * Define EM() and EMe() so that MIGRATE_REASON from trace/events/migrate.h can
2362306a36Sopenharmony_ci * be used to populate migrate_reason_names[].
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci#undef EM
2662306a36Sopenharmony_ci#undef EMe
2762306a36Sopenharmony_ci#define EM(a, b)	b,
2862306a36Sopenharmony_ci#define EMe(a, b)	b
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciconst char *migrate_reason_names[MR_TYPES] = {
3162306a36Sopenharmony_ci	MIGRATE_REASON
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciconst struct trace_print_flags pageflag_names[] = {
3562306a36Sopenharmony_ci	__def_pageflag_names,
3662306a36Sopenharmony_ci	{0, NULL}
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciconst struct trace_print_flags pagetype_names[] = {
4062306a36Sopenharmony_ci	__def_pagetype_names,
4162306a36Sopenharmony_ci	{0, NULL}
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciconst struct trace_print_flags gfpflag_names[] = {
4562306a36Sopenharmony_ci	__def_gfpflag_names,
4662306a36Sopenharmony_ci	{0, NULL}
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ciconst struct trace_print_flags vmaflag_names[] = {
5062306a36Sopenharmony_ci	__def_vmaflag_names,
5162306a36Sopenharmony_ci	{0, NULL}
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void __dump_page(struct page *page)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct folio *folio = page_folio(page);
5762306a36Sopenharmony_ci	struct page *head = &folio->page;
5862306a36Sopenharmony_ci	struct address_space *mapping;
5962306a36Sopenharmony_ci	bool compound = PageCompound(page);
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * Accessing the pageblock without the zone lock. It could change to
6262306a36Sopenharmony_ci	 * "isolate" again in the meantime, but since we are just dumping the
6362306a36Sopenharmony_ci	 * state for debugging, it should be fine to accept a bit of
6462306a36Sopenharmony_ci	 * inaccuracy here due to racing.
6562306a36Sopenharmony_ci	 */
6662306a36Sopenharmony_ci	bool page_cma = is_migrate_cma_page(page);
6762306a36Sopenharmony_ci	int mapcount;
6862306a36Sopenharmony_ci	char *type = "";
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (page < head || (page >= head + MAX_ORDER_NR_PAGES)) {
7162306a36Sopenharmony_ci		/*
7262306a36Sopenharmony_ci		 * Corrupt page, so we cannot call page_mapping. Instead, do a
7362306a36Sopenharmony_ci		 * safe subset of the steps that page_mapping() does. Caution:
7462306a36Sopenharmony_ci		 * this will be misleading for tail pages, PageSwapCache pages,
7562306a36Sopenharmony_ci		 * and potentially other situations. (See the page_mapping()
7662306a36Sopenharmony_ci		 * implementation for what's missing here.)
7762306a36Sopenharmony_ci		 */
7862306a36Sopenharmony_ci		unsigned long tmp = (unsigned long)page->mapping;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		if (tmp & PAGE_MAPPING_ANON)
8162306a36Sopenharmony_ci			mapping = NULL;
8262306a36Sopenharmony_ci		else
8362306a36Sopenharmony_ci			mapping = (void *)(tmp & ~PAGE_MAPPING_FLAGS);
8462306a36Sopenharmony_ci		head = page;
8562306a36Sopenharmony_ci		folio = (struct folio *)page;
8662306a36Sopenharmony_ci		compound = false;
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		mapping = page_mapping(page);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * Avoid VM_BUG_ON() in page_mapcount().
9362306a36Sopenharmony_ci	 * page->_mapcount space in struct page is used by sl[aou]b pages to
9462306a36Sopenharmony_ci	 * encode own info.
9562306a36Sopenharmony_ci	 */
9662306a36Sopenharmony_ci	mapcount = PageSlab(head) ? 0 : page_mapcount(page);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	pr_warn("page:%p refcount:%d mapcount:%d mapping:%p index:%#lx pfn:%#lx\n",
9962306a36Sopenharmony_ci			page, page_ref_count(head), mapcount, mapping,
10062306a36Sopenharmony_ci			page_to_pgoff(page), page_to_pfn(page));
10162306a36Sopenharmony_ci	if (compound) {
10262306a36Sopenharmony_ci		pr_warn("head:%p order:%u entire_mapcount:%d nr_pages_mapped:%d pincount:%d\n",
10362306a36Sopenharmony_ci				head, compound_order(head),
10462306a36Sopenharmony_ci				folio_entire_mapcount(folio),
10562306a36Sopenharmony_ci				folio_nr_pages_mapped(folio),
10662306a36Sopenharmony_ci				atomic_read(&folio->_pincount));
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#ifdef CONFIG_MEMCG
11062306a36Sopenharmony_ci	if (head->memcg_data)
11162306a36Sopenharmony_ci		pr_warn("memcg:%lx\n", head->memcg_data);
11262306a36Sopenharmony_ci#endif
11362306a36Sopenharmony_ci	if (PageKsm(page))
11462306a36Sopenharmony_ci		type = "ksm ";
11562306a36Sopenharmony_ci	else if (PageAnon(page))
11662306a36Sopenharmony_ci		type = "anon ";
11762306a36Sopenharmony_ci	else if (mapping)
11862306a36Sopenharmony_ci		dump_mapping(mapping);
11962306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS + 1);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	pr_warn("%sflags: %pGp%s\n", type, &head->flags,
12262306a36Sopenharmony_ci		page_cma ? " CMA" : "");
12362306a36Sopenharmony_ci	pr_warn("page_type: %pGt\n", &head->page_type);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	print_hex_dump(KERN_WARNING, "raw: ", DUMP_PREFIX_NONE, 32,
12662306a36Sopenharmony_ci			sizeof(unsigned long), page,
12762306a36Sopenharmony_ci			sizeof(struct page), false);
12862306a36Sopenharmony_ci	if (head != page)
12962306a36Sopenharmony_ci		print_hex_dump(KERN_WARNING, "head: ", DUMP_PREFIX_NONE, 32,
13062306a36Sopenharmony_ci			sizeof(unsigned long), head,
13162306a36Sopenharmony_ci			sizeof(struct page), false);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_civoid dump_page(struct page *page, const char *reason)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	if (PagePoisoned(page))
13762306a36Sopenharmony_ci		pr_warn("page:%p is uninitialized and poisoned", page);
13862306a36Sopenharmony_ci	else
13962306a36Sopenharmony_ci		__dump_page(page);
14062306a36Sopenharmony_ci	if (reason)
14162306a36Sopenharmony_ci		pr_warn("page dumped because: %s\n", reason);
14262306a36Sopenharmony_ci	dump_page_owner(page);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ciEXPORT_SYMBOL(dump_page);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_VM
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_civoid dump_vma(const struct vm_area_struct *vma)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	pr_emerg("vma %px start %px end %px mm %px\n"
15162306a36Sopenharmony_ci		"prot %lx anon_vma %px vm_ops %px\n"
15262306a36Sopenharmony_ci		"pgoff %lx file %px private_data %px\n"
15362306a36Sopenharmony_ci		"flags: %#lx(%pGv)\n",
15462306a36Sopenharmony_ci		vma, (void *)vma->vm_start, (void *)vma->vm_end, vma->vm_mm,
15562306a36Sopenharmony_ci		(unsigned long)pgprot_val(vma->vm_page_prot),
15662306a36Sopenharmony_ci		vma->anon_vma, vma->vm_ops, vma->vm_pgoff,
15762306a36Sopenharmony_ci		vma->vm_file, vma->vm_private_data,
15862306a36Sopenharmony_ci		vma->vm_flags, &vma->vm_flags);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ciEXPORT_SYMBOL(dump_vma);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_civoid dump_mm(const struct mm_struct *mm)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	pr_emerg("mm %px task_size %lu\n"
16562306a36Sopenharmony_ci#ifdef CONFIG_MMU
16662306a36Sopenharmony_ci		"get_unmapped_area %px\n"
16762306a36Sopenharmony_ci#endif
16862306a36Sopenharmony_ci		"mmap_base %lu mmap_legacy_base %lu\n"
16962306a36Sopenharmony_ci		"pgd %px mm_users %d mm_count %d pgtables_bytes %lu map_count %d\n"
17062306a36Sopenharmony_ci		"hiwater_rss %lx hiwater_vm %lx total_vm %lx locked_vm %lx\n"
17162306a36Sopenharmony_ci		"pinned_vm %llx data_vm %lx exec_vm %lx stack_vm %lx\n"
17262306a36Sopenharmony_ci		"start_code %lx end_code %lx start_data %lx end_data %lx\n"
17362306a36Sopenharmony_ci		"start_brk %lx brk %lx start_stack %lx\n"
17462306a36Sopenharmony_ci		"arg_start %lx arg_end %lx env_start %lx env_end %lx\n"
17562306a36Sopenharmony_ci		"binfmt %px flags %lx\n"
17662306a36Sopenharmony_ci#ifdef CONFIG_AIO
17762306a36Sopenharmony_ci		"ioctx_table %px\n"
17862306a36Sopenharmony_ci#endif
17962306a36Sopenharmony_ci#ifdef CONFIG_MEMCG
18062306a36Sopenharmony_ci		"owner %px "
18162306a36Sopenharmony_ci#endif
18262306a36Sopenharmony_ci		"exe_file %px\n"
18362306a36Sopenharmony_ci#ifdef CONFIG_MMU_NOTIFIER
18462306a36Sopenharmony_ci		"notifier_subscriptions %px\n"
18562306a36Sopenharmony_ci#endif
18662306a36Sopenharmony_ci#ifdef CONFIG_NUMA_BALANCING
18762306a36Sopenharmony_ci		"numa_next_scan %lu numa_scan_offset %lu numa_scan_seq %d\n"
18862306a36Sopenharmony_ci#endif
18962306a36Sopenharmony_ci		"tlb_flush_pending %d\n"
19062306a36Sopenharmony_ci		"def_flags: %#lx(%pGv)\n",
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		mm, mm->task_size,
19362306a36Sopenharmony_ci#ifdef CONFIG_MMU
19462306a36Sopenharmony_ci		mm->get_unmapped_area,
19562306a36Sopenharmony_ci#endif
19662306a36Sopenharmony_ci		mm->mmap_base, mm->mmap_legacy_base,
19762306a36Sopenharmony_ci		mm->pgd, atomic_read(&mm->mm_users),
19862306a36Sopenharmony_ci		atomic_read(&mm->mm_count),
19962306a36Sopenharmony_ci		mm_pgtables_bytes(mm),
20062306a36Sopenharmony_ci		mm->map_count,
20162306a36Sopenharmony_ci		mm->hiwater_rss, mm->hiwater_vm, mm->total_vm, mm->locked_vm,
20262306a36Sopenharmony_ci		(u64)atomic64_read(&mm->pinned_vm),
20362306a36Sopenharmony_ci		mm->data_vm, mm->exec_vm, mm->stack_vm,
20462306a36Sopenharmony_ci		mm->start_code, mm->end_code, mm->start_data, mm->end_data,
20562306a36Sopenharmony_ci		mm->start_brk, mm->brk, mm->start_stack,
20662306a36Sopenharmony_ci		mm->arg_start, mm->arg_end, mm->env_start, mm->env_end,
20762306a36Sopenharmony_ci		mm->binfmt, mm->flags,
20862306a36Sopenharmony_ci#ifdef CONFIG_AIO
20962306a36Sopenharmony_ci		mm->ioctx_table,
21062306a36Sopenharmony_ci#endif
21162306a36Sopenharmony_ci#ifdef CONFIG_MEMCG
21262306a36Sopenharmony_ci		mm->owner,
21362306a36Sopenharmony_ci#endif
21462306a36Sopenharmony_ci		mm->exe_file,
21562306a36Sopenharmony_ci#ifdef CONFIG_MMU_NOTIFIER
21662306a36Sopenharmony_ci		mm->notifier_subscriptions,
21762306a36Sopenharmony_ci#endif
21862306a36Sopenharmony_ci#ifdef CONFIG_NUMA_BALANCING
21962306a36Sopenharmony_ci		mm->numa_next_scan, mm->numa_scan_offset, mm->numa_scan_seq,
22062306a36Sopenharmony_ci#endif
22162306a36Sopenharmony_ci		atomic_read(&mm->tlb_flush_pending),
22262306a36Sopenharmony_ci		mm->def_flags, &mm->def_flags
22362306a36Sopenharmony_ci	);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ciEXPORT_SYMBOL(dump_mm);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic bool page_init_poisoning __read_mostly = true;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int __init setup_vm_debug(char *str)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	bool __page_init_poisoning = true;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/*
23462306a36Sopenharmony_ci	 * Calling vm_debug with no arguments is equivalent to requesting
23562306a36Sopenharmony_ci	 * to enable all debugging options we can control.
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	if (*str++ != '=' || !*str)
23862306a36Sopenharmony_ci		goto out;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	__page_init_poisoning = false;
24162306a36Sopenharmony_ci	if (*str == '-')
24262306a36Sopenharmony_ci		goto out;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	while (*str) {
24562306a36Sopenharmony_ci		switch (tolower(*str)) {
24662306a36Sopenharmony_ci		case'p':
24762306a36Sopenharmony_ci			__page_init_poisoning = true;
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci		default:
25062306a36Sopenharmony_ci			pr_err("vm_debug option '%c' unknown. skipped\n",
25162306a36Sopenharmony_ci			       *str);
25262306a36Sopenharmony_ci		}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		str++;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ciout:
25762306a36Sopenharmony_ci	if (page_init_poisoning && !__page_init_poisoning)
25862306a36Sopenharmony_ci		pr_warn("Page struct poisoning disabled by kernel command line option 'vm_debug'\n");
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	page_init_poisoning = __page_init_poisoning;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return 1;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci__setup("vm_debug", setup_vm_debug);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_civoid page_init_poison(struct page *page, size_t size)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	if (page_init_poisoning)
26962306a36Sopenharmony_ci		memset(page, PAGE_POISON_PATTERN, size);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_civoid vma_iter_dump_tree(const struct vma_iterator *vmi)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_VM_MAPLE_TREE)
27562306a36Sopenharmony_ci	mas_dump(&vmi->mas);
27662306a36Sopenharmony_ci	mt_dump(vmi->mas.tree, mt_dump_hex);
27762306a36Sopenharmony_ci#endif	/* CONFIG_DEBUG_VM_MAPLE_TREE */
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci#endif		/* CONFIG_DEBUG_VM */
281