Re: [PATCH v3 17/21] mm/mm_init: don't rely on memblock to get KHO scratch migratetype
From: Pratyush Yadav
Date: Sat Jul 25 2026 - 11:56:27 EST
On Wed, Jul 15 2026, Mike Rapoport wrote:
>> Currently struct page init via memmap_init() or deferred_init_memmap()
>> only queries the migrate type from KHO for each discrete memory range.
>> That works currently since KHO scratch memory has a different memory
>> type so it is always it its own region.
>>
>> An upcoming patch will add support for discovering blocks of memory with
>> no preservations and it will mark it as MEMBLOCK_KHO_SCRATCH to allow
>> allocations from them. This can lead to the bootmem KHO scratch areas to
>> be merged into larger free ranges. This merging breaks the selection of
>> migrate type.
>>
>> Get rid of kho_scratch_migratetype(). Instead, let the struct page init
>> logic set the migratetype to MIGRATE_MOVABLE for all pages, and then
>> give KHO a chance to update the migrate type of its bootmem scratch
>> areas to MIGRATE_CMA. Since scratch areas should be a few order of
>> magnitues smaller than the total system memory, doing it this way is
>> more efficient than searching the kho_scratch array for every pageblock.
>
> It's true for non-deferred case, but with deferred initialization of
> struct pages the static part is small and deferred chunks are small, so
> we are going to update the migratetype nearly as much as we would check
> it with kho_scratch_migratetype().
>
> For simplicity of this patchset I'd keep kho_scratch_migratetype(), just
> move it over to kexec_handover.h and make it rely on kho_scratch_overlap().
>
> The bulk optimization can be a separate patch on top.
Sure, will do.
>
>> Since kho_scratch_migratetype() no longer exists, drop the migratetype
>> argument to memmap_init_zone_range() and deferred_init_pages().
>>
>> Signed-off-by: Pratyush Yadav (Google) <pratyush@xxxxxxxxxx>
>>
[...]
>> +/*
>> + * Mark all KHO scratch pageblocks between start_pfn and end_pfn as
>> + * MIGRATE_CMA.
>> + */
>> +void kho_init_scratch_migratetype(unsigned long start_pfn, unsigned long end_pfn)
>> +{
>> + for (unsigned int i = 0; i < kho_scratch_cnt; i++) {
>> + unsigned long scratch_start, scratch_end, pfn;
>> +
>> + scratch_start = PHYS_PFN(kho_scratch[i].addr);
>> + scratch_end = PHYS_PFN(kho_scratch[i].addr + kho_scratch[i].size);
>> +
>> + /* Target range doesn't overlap with scratch. */
>> + if (start_pfn >= scratch_end || end_pfn <= scratch_start)
>> + continue;
>> +
>> + pfn = max_t(unsigned long, start_pfn, scratch_start);
>> + pfn = pageblock_start_pfn(pfn);
>> +
>> + while (pfn < end_pfn && pfn < scratch_end) {
>> + init_pageblock_migratetype(pfn_to_page(pfn),
>> + MIGRATE_CMA, false);
>> + pfn += pageblock_nr_pages;
>> + }
>> + }
>> +}
>
> static inline enum migratetype kho_scratch_migratetype(unsigned long pfn,
> enum migratetype mt)
> {
> if (kho_scratch_overlap(PFN_PHYS(pfn)), PAGE_SIZE)
> return MIGRATE_CMA;
> return mt;
> }
>
> Is way easier to parse and reason about ;-)
>
>> +
>> /**
>> * kho_reserve_scratch - Reserve a contiguous chunk of memory for kexec
>> *
[...]
>> @@ -2012,8 +2021,7 @@ static inline void __init pgdat_init_report_one_done(void)
>> * Return number of pages initialized.
>> */
>> static unsigned long __init deferred_init_pages(struct zone *zone,
>> - unsigned long start_pfn, unsigned long end_pfn,
>> - enum migratetype mt)
>> + unsigned long start_pfn, unsigned long end_pfn)
>> {
>> int nid = zone_to_nid(zone);
>> unsigned long nr_pages = end_pfn - start_pfn, pfn = start_pfn;
>> @@ -2027,7 +2035,14 @@ static unsigned long __init deferred_init_pages(struct zone *zone,
>> pfn = pageblock_align(start_pfn);
>> page = pfn_to_page(pfn);
>> for (; pfn < end_pfn; pfn += pageblock_nr_pages, page += pageblock_nr_pages)
>> - init_pageblock_migratetype(page, mt, false);
>> + init_pageblock_migratetype(page, MIGRATE_MOVABLE, false);
>> +
>> + /*
>> + * Update the migrate type for any pageblocks that fall in a KHO scratch
>> + * area. Since most pageblocks will not be KHO scratch, do it outside
>> + * the loop to only do the search once.
>> + */
>> + kho_init_scratch_migratetype(start_pfn, end_pfn);
>
> And here the chunks are at most MAX_ORDER_NR_PAGES, so bulk init also
> wouldn't save much.
Hmm, right. I didn't think of that. On my system pageblock order is 9
and max page order is 10, so it does halve the amount of work done, but
still, I see your point. It took me a couple of tries to get
kho_init_scratch_migratetype() right, and kho_scratch_overlap() is
certainly easier to reason about.
Oh well, I suppose this gets added to the list of performance
improvements we do at some point down the line
--
Regards,
Pratyush Yadav