show_mem()'s per-order line includes a parenthesized set of letters (UME, etc.) indicating which migratetypes have free pages at that order. This was computed by checking free_area_empty() on zone->free_area[order].free_list[type]. After the SPB rework, those zone-level list heads are always empty -- free pages live on per- superpageblock lists -- so the migratetype letters never appeared. Iterate every SPB in the zone for each order, OR'ing in any non-empty migratetype lists, with an early exit once all migratetypes have been seen. The shadow nr_free count remains correct (zone->free_area[]. nr_free is updated by __add_to_free_list / __del_page_from_free_list to sum across all SPBs). Falls back to the zone-level free_area for zones whose SPB array has not yet been allocated. The whole loop runs under spin_lock_irqsave(&zone->lock) without drops, so no hotplug race. Worst case work is bounded (NR_PAGE_ORDERS * MIGRATE_TYPES * nr_superpageblocks list_empty pointer compares per zone) and acceptable for a diagnostic path. Signed-off-by: Rik van Riel Assisted-by: Claude:claude-opus-4.7 syzkaller --- mm/show_mem.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/mm/show_mem.c b/mm/show_mem.c index d08f1263480a..ce7f43416199 100644 --- a/mm/show_mem.c +++ b/mm/show_mem.c @@ -367,16 +367,31 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z spin_lock_irqsave(&zone->lock, flags); for (order = 0; order < NR_PAGE_ORDERS; order++) { - struct free_area *area = &zone->free_area[order]; + unsigned long sb_idx; + unsigned long nr_lists = zone->nr_superpageblocks ? : 1; int type; - nr[order] = area->nr_free; + nr[order] = zone->free_area[order].nr_free; total += nr[order] << order; + /* + * Collect the migratetypes present at this order. After + * the SPB rework, free pages live on per-superpageblock + * free lists, so check each SPB. Stop early once all + * migratetypes have been observed. + */ types[order] = 0; - for (type = 0; type < MIGRATE_TYPES; type++) { - if (!free_area_empty(area, type)) - types[order] |= 1 << type; + for (sb_idx = 0; sb_idx < nr_lists; sb_idx++) { + struct free_area *area = zone->nr_superpageblocks ? + &zone->superpageblocks[sb_idx].free_area[order] : + &zone->free_area[order]; + + for (type = 0; type < MIGRATE_TYPES; type++) { + if (!free_area_empty(area, type)) + types[order] |= 1 << type; + } + if (types[order] == (1 << MIGRATE_TYPES) - 1) + break; } } spin_unlock_irqrestore(&zone->lock, flags); -- 2.54.0