mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	mm: add __dump_folio()
Turn __dump_page() into a wrapper around __dump_folio(). Snapshot the page & folio into a stack variable so we don't hit BUG_ON() if an allocation is freed under us and what was a folio pointer becomes a pointer to a tail page. [willy@infradead.org: fix build issue] Link: https://lkml.kernel.org/r/ZeAKCyTn_xS3O9cE@casper.infradead.org [willy@infradead.org: fix __dump_folio] Link: https://lkml.kernel.org/r/ZeJJegP8zM7S9GTy@casper.infradead.org [willy@infradead.org: fix pointer confusion] Link: https://lkml.kernel.org/r/ZeYa00ixxC4k1ot-@casper.infradead.org [akpm@linux-foundation.org: s/printk/pr_warn/] Link: https://lkml.kernel.org/r/20240227192337.757313-5-willy@infradead.org Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									7da8988c7c
								
							
						
					
					
						commit
						fae7d834c4
					
				
					 3 changed files with 83 additions and 55 deletions
				
			
		| 
						 | 
				
			
			@ -2066,6 +2066,13 @@ static inline long folio_nr_pages(struct folio *folio)
 | 
			
		|||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Only hugetlbfs can allocate folios larger than MAX_ORDER */
 | 
			
		||||
#ifdef CONFIG_ARCH_HAS_GIGANTIC_PAGE
 | 
			
		||||
#define MAX_FOLIO_NR_PAGES	(1UL << PUD_ORDER)
 | 
			
		||||
#else
 | 
			
		||||
#define MAX_FOLIO_NR_PAGES	MAX_ORDER_NR_PAGES
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * compound_nr() returns the number of pages in this potentially compound
 | 
			
		||||
 * page.  compound_nr() can be called on a tail page, and is defined to
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,9 +76,12 @@ extern const char * const migratetype_names[MIGRATE_TYPES];
 | 
			
		|||
#ifdef CONFIG_CMA
 | 
			
		||||
#  define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
 | 
			
		||||
#  define is_migrate_cma_page(_page) (get_pageblock_migratetype(_page) == MIGRATE_CMA)
 | 
			
		||||
#  define is_migrate_cma_folio(folio, pfn)	(MIGRATE_CMA ==		\
 | 
			
		||||
	get_pfnblock_flags_mask(&folio->page, pfn, MIGRATETYPE_MASK))
 | 
			
		||||
#else
 | 
			
		||||
#  define is_migrate_cma(migratetype) false
 | 
			
		||||
#  define is_migrate_cma_page(_page) false
 | 
			
		||||
#  define is_migrate_cma_folio(folio, pfn) false
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static inline bool is_migrate_movable(int mt)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										124
									
								
								mm/debug.c
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								mm/debug.c
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -51,84 +51,102 @@ const struct trace_print_flags vmaflag_names[] = {
 | 
			
		|||
	{0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void __dump_page(struct page *page)
 | 
			
		||||
static void __dump_folio(struct folio *folio, struct page *page,
 | 
			
		||||
		unsigned long pfn, unsigned long idx)
 | 
			
		||||
{
 | 
			
		||||
	struct folio *folio = page_folio(page);
 | 
			
		||||
	struct page *head = &folio->page;
 | 
			
		||||
	struct address_space *mapping;
 | 
			
		||||
	bool compound = PageCompound(page);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Accessing the pageblock without the zone lock. It could change to
 | 
			
		||||
	 * "isolate" again in the meantime, but since we are just dumping the
 | 
			
		||||
	 * state for debugging, it should be fine to accept a bit of
 | 
			
		||||
	 * inaccuracy here due to racing.
 | 
			
		||||
	 */
 | 
			
		||||
	bool page_cma = is_migrate_cma_page(page);
 | 
			
		||||
	int mapcount;
 | 
			
		||||
	struct address_space *mapping = folio_mapping(folio);
 | 
			
		||||
	int mapcount = 0;
 | 
			
		||||
	char *type = "";
 | 
			
		||||
 | 
			
		||||
	if (page < head || (page >= head + MAX_ORDER_NR_PAGES)) {
 | 
			
		||||
	/*
 | 
			
		||||
		 * Corrupt page, so we cannot call page_mapping. Instead, do a
 | 
			
		||||
		 * safe subset of the steps that page_mapping() does. Caution:
 | 
			
		||||
		 * this will be misleading for tail pages, PageSwapCache pages,
 | 
			
		||||
		 * and potentially other situations. (See the page_mapping()
 | 
			
		||||
		 * implementation for what's missing here.)
 | 
			
		||||
	 * page->_mapcount space in struct page is used by slab pages to
 | 
			
		||||
	 * encode own info, and we must avoid calling page_folio() again.
 | 
			
		||||
	 */
 | 
			
		||||
		unsigned long tmp = (unsigned long)page->mapping;
 | 
			
		||||
 | 
			
		||||
		if (tmp & PAGE_MAPPING_ANON)
 | 
			
		||||
			mapping = NULL;
 | 
			
		||||
		else
 | 
			
		||||
			mapping = (void *)(tmp & ~PAGE_MAPPING_FLAGS);
 | 
			
		||||
		head = page;
 | 
			
		||||
		folio = (struct folio *)page;
 | 
			
		||||
		compound = false;
 | 
			
		||||
	} else {
 | 
			
		||||
		mapping = page_mapping(page);
 | 
			
		||||
	if (!folio_test_slab(folio)) {
 | 
			
		||||
		mapcount = atomic_read(&page->_mapcount) + 1;
 | 
			
		||||
		if (folio_test_large(folio))
 | 
			
		||||
			mapcount += folio_entire_mapcount(folio);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Avoid VM_BUG_ON() in page_mapcount().
 | 
			
		||||
	 * page->_mapcount space in struct page is used by sl[aou]b pages to
 | 
			
		||||
	 * encode own info.
 | 
			
		||||
	 */
 | 
			
		||||
	mapcount = PageSlab(head) ? 0 : page_mapcount(page);
 | 
			
		||||
 | 
			
		||||
	pr_warn("page:%p refcount:%d mapcount:%d mapping:%p index:%#lx pfn:%#lx\n",
 | 
			
		||||
			page, page_ref_count(head), mapcount, mapping,
 | 
			
		||||
			page_to_pgoff(page), page_to_pfn(page));
 | 
			
		||||
	if (compound) {
 | 
			
		||||
		pr_warn("head:%p order:%u entire_mapcount:%d nr_pages_mapped:%d pincount:%d\n",
 | 
			
		||||
				head, compound_order(head),
 | 
			
		||||
	pr_warn("page: refcount:%d mapcount:%d mapping:%p index:%#lx pfn:%#lx\n",
 | 
			
		||||
			folio_ref_count(folio), mapcount, mapping,
 | 
			
		||||
			folio->index + idx, pfn);
 | 
			
		||||
	if (folio_test_large(folio)) {
 | 
			
		||||
		pr_warn("head: order:%u entire_mapcount:%d nr_pages_mapped:%d pincount:%d\n",
 | 
			
		||||
				folio_order(folio),
 | 
			
		||||
				folio_entire_mapcount(folio),
 | 
			
		||||
				folio_nr_pages_mapped(folio),
 | 
			
		||||
				atomic_read(&folio->_pincount));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MEMCG
 | 
			
		||||
	if (head->memcg_data)
 | 
			
		||||
		pr_warn("memcg:%lx\n", head->memcg_data);
 | 
			
		||||
	if (folio->memcg_data)
 | 
			
		||||
		pr_warn("memcg:%lx\n", folio->memcg_data);
 | 
			
		||||
#endif
 | 
			
		||||
	if (PageKsm(page))
 | 
			
		||||
	if (folio_test_ksm(folio))
 | 
			
		||||
		type = "ksm ";
 | 
			
		||||
	else if (PageAnon(page))
 | 
			
		||||
	else if (folio_test_anon(folio))
 | 
			
		||||
		type = "anon ";
 | 
			
		||||
	else if (mapping)
 | 
			
		||||
		dump_mapping(mapping);
 | 
			
		||||
	BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS + 1);
 | 
			
		||||
 | 
			
		||||
	pr_warn("%sflags: %pGp%s\n", type, &head->flags,
 | 
			
		||||
		page_cma ? " CMA" : "");
 | 
			
		||||
	pr_warn("page_type: %pGt\n", &head->page_type);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Accessing the pageblock without the zone lock. It could change to
 | 
			
		||||
	 * "isolate" again in the meantime, but since we are just dumping the
 | 
			
		||||
	 * state for debugging, it should be fine to accept a bit of
 | 
			
		||||
	 * inaccuracy here due to racing.
 | 
			
		||||
	 */
 | 
			
		||||
	pr_warn("%sflags: %pGp%s\n", type, &folio->flags,
 | 
			
		||||
		is_migrate_cma_folio(folio, pfn) ? " CMA" : "");
 | 
			
		||||
	pr_warn("page_type: %pGt\n", &folio->page.page_type);
 | 
			
		||||
 | 
			
		||||
	print_hex_dump(KERN_WARNING, "raw: ", DUMP_PREFIX_NONE, 32,
 | 
			
		||||
			sizeof(unsigned long), page,
 | 
			
		||||
			sizeof(struct page), false);
 | 
			
		||||
	if (head != page)
 | 
			
		||||
	if (folio_test_large(folio))
 | 
			
		||||
		print_hex_dump(KERN_WARNING, "head: ", DUMP_PREFIX_NONE, 32,
 | 
			
		||||
			sizeof(unsigned long), head,
 | 
			
		||||
			sizeof(struct page), false);
 | 
			
		||||
			sizeof(unsigned long), folio,
 | 
			
		||||
			2 * sizeof(struct page), false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __dump_page(const struct page *page)
 | 
			
		||||
{
 | 
			
		||||
	struct folio *foliop, folio;
 | 
			
		||||
	struct page precise;
 | 
			
		||||
	unsigned long pfn = page_to_pfn(page);
 | 
			
		||||
	unsigned long idx, nr_pages = 1;
 | 
			
		||||
	int loops = 5;
 | 
			
		||||
 | 
			
		||||
again:
 | 
			
		||||
	memcpy(&precise, page, sizeof(*page));
 | 
			
		||||
	foliop = page_folio(&precise);
 | 
			
		||||
	if (foliop == (struct folio *)&precise) {
 | 
			
		||||
		idx = 0;
 | 
			
		||||
		if (!folio_test_large(foliop))
 | 
			
		||||
			goto dump;
 | 
			
		||||
		foliop = (struct folio *)page;
 | 
			
		||||
	} else {
 | 
			
		||||
		idx = folio_page_idx(foliop, page);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (idx < MAX_FOLIO_NR_PAGES) {
 | 
			
		||||
		memcpy(&folio, foliop, 2 * sizeof(struct page));
 | 
			
		||||
		nr_pages = folio_nr_pages(&folio);
 | 
			
		||||
		foliop = &folio;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (idx > nr_pages) {
 | 
			
		||||
		if (loops-- > 0)
 | 
			
		||||
			goto again;
 | 
			
		||||
		pr_warn("page does not match folio\n");
 | 
			
		||||
		precise.compound_head &= ~1UL;
 | 
			
		||||
		foliop = (struct folio *)&precise;
 | 
			
		||||
		idx = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
dump:
 | 
			
		||||
	__dump_folio(foliop, &precise, pfn, idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dump_page(struct page *page, const char *reason)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue