Boot-time HugeTLB allocation has to initialize tail struct pages itself when HugeTLB vmemmap optimization (HVO) is unavailable or fails. For gigantic pages, this can cover a large number of tail pages. Most state installed by __init_single_page() and prep_compound_tail() is identical for every tail page in the same huge page; only PFN-dependent fields need refreshing. Initialize the first tail page normally, copy it into a local template, then initialize the remaining tails from that template with memmap_init_page_from_template(). This avoids repeatedly doing the full __init_single_page() and prep_compound_tail() sequence for state that is identical across tail pages in the same huge page. On an arm64 QEMU/KVM guest using 4K pages and 64 boot-time 1G hugepages (HVO unavailable on arm64 in v7.4-rc1), the measured fallback tail initialization time improved by 36.43%, measured with a temporary jiffies-based printk modeled after the existing timing in memmap_init_zone_device(): base (v7.4-rc1): 318.4 ms with this patch: 202.4 ms Signed-off-by: Li Zhe Acked-by: Balbir Singh --- mm/hugetlb.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 630c96cd1e88..3f6731161d77 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -3223,6 +3223,7 @@ static void __init hugetlb_folio_init_tail_vmemmap(struct folio *folio, enum zone_type zone = folio_zonenum(folio); int nid = folio_nid(folio); struct page *page = folio_page(folio, start_page_number); + struct page template; unsigned long head_pfn = folio_pfn(folio); unsigned long pfn, end_pfn = head_pfn + end_page_number; unsigned int order = huge_page_order(h); @@ -3231,11 +3232,20 @@ static void __init hugetlb_folio_init_tail_vmemmap(struct folio *folio, * As we marked all tail pages with memblock_reserved_mark_noinit(), * we must initialize them ourselves here. */ - for (pfn = head_pfn + start_page_number; pfn < end_pfn; page++, pfn++) { - __init_single_page(page, pfn, zone, nid); - prep_compound_tail(page, &folio->page, order); - set_page_count(page, 0); - } + pfn = head_pfn + start_page_number; + + /* + * Seed a template from the first real tail page and refresh only + * PFN-dependent fields before copying it. + */ + __init_single_page(page, pfn, zone, nid); + prep_compound_tail(page, &folio->page, order); + set_page_count(page, 0); + memcpy(&template, page, sizeof(template)); + + for (pfn++; pfn < end_pfn; pfn++) + memmap_init_page_from_template(pfn_to_page(pfn), pfn, zone, + &template); } static void __init hugetlb_folio_init_vmemmap(struct folio *folio, -- 2.20.1