kho_preserve_pages() uses __kho_preserve_pages_order() to reduce the order of a preservation so that it does not span multiple NUMA nodes. kho_restore_pages() does not use that helper; it recomputes the order from the pfn alignment and the remaining length only. When a preservation was split at a NUMA boundary, restore picks a larger order than preserve did and jumps over the next key. Those pages never get restored: they stay memblock-reserved and keep the KHOP magic in page->private, while kho_restore_pages() returns success for the pages that were restored. The consumer has no way to notice that part of its buffer was not restored. Fix this by reading the order from the key being restored. The order is already stored in page->private by kho_preserved_memory_reserve() and is validated by kho_restore_page(). Advance by that order instead of recomputing it. This also keeps restore independent of the incoming kernel's NUMA topology. Example: QEMU is started with two NUMA nodes: node0: [0x40000000, 0xC0400000) 2052 MiB node1: [0xC0400000, 0x1C0000000) 4092 MiB The module preserves [0xC0000000, 0xC0800000) (8 MiB, 2048 pages). The range is 8 MiB aligned and the node boundary sits exactly 4 MiB into it, so preserve emits two order-10 keys, the first at 0xC0000000 and the second at 0xC0400000. Restore computes order 11 for the first page, restores the first key and jumps straight to the end of the range, skipping the second key. Fixes: 91e74fa8b1bc ("kho: make sure preservations do not span multiple NUMA nodes") Signed-off-by: Mukesh Pilaniya --- kernel/liveupdate/kexec_handover.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 6f1f55fe3181..7d30ad9cc512 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -511,7 +511,8 @@ static void kho_init_folio(struct page *page, unsigned int order) prep_compound_page(page, order); } -static struct page *kho_restore_page(phys_addr_t phys, bool is_folio) +static struct page *kho_restore_page(phys_addr_t phys, bool is_folio, + unsigned int *order) { struct page *page = pfn_to_online_page(PHYS_PFN(phys)); unsigned long nr_pages; @@ -530,6 +531,9 @@ static struct page *kho_restore_page(phys_addr_t phys, bool is_folio) return NULL; nr_pages = (1 << info.order); + if (order) + *order = info.order; + /* Clear private to make sure later restores on this page error out. */ page->private = 0; @@ -550,7 +554,7 @@ static struct page *kho_restore_page(phys_addr_t phys, bool is_folio) */ struct folio *kho_restore_folio(phys_addr_t phys) { - struct page *page = kho_restore_page(phys, true); + struct page *page = kho_restore_page(phys, true, NULL); return page ? page_folio(page) : NULL; } @@ -573,13 +577,13 @@ struct page *kho_restore_pages(phys_addr_t phys, unsigned long nr_pages) unsigned long pfn = start_pfn; while (pfn < end_pfn) { - const unsigned int order = - min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn)); - struct page *page = kho_restore_page(PFN_PHYS(pfn), false); + unsigned int order; + struct page *page; + page = kho_restore_page(PFN_PHYS(pfn), false, &order); if (!page) return NULL; - pfn += 1 << order; + pfn += 1UL << order; } return pfn_to_page(start_pfn); --- base-commit: 2637bdec0535a2044cd830eba9a79363651d9043 change-id: 20261103-kho-numa-5ce38a3ed9c9 Best regards, -- Mukesh Pilaniya