Re: [RFC PATCH v2 2/2] kho: Introduce preserve/restore APIs for unsplit pages
From: Mike Rapoport
Date: Mon Jul 20 2026 - 13:02:22 EST
> The current KHO page preservation APIs (e.g. kho_preserve_pages) assume
> that multi-page blocks are split into independent 4KB pages during
> restoration. This is incompatible with high-order non-compound pages,
> such as DMA buffers, which must be restored with tail pages having a
> zero reference count.
>
> Introduce explicit preserve and restore APIs for unsplit pages,
high-order non-compound ^
> which preserve and restore a high-order page block as a single unit,
> applying a refcount of 1 to the head page while leaving tail pages at 0.
> Rename the existing internal helper to __kho_restore_page().
>
> Signed-off-by: Pranjal Shrivastava <praan@xxxxxxxxxx>
>
> diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h
> index 8968c56d2d73e..6e2c75b16209c 100644
> --- a/include/linux/kexec_handover.h
> +++ b/include/linux/kexec_handover.h
> @@ -22,6 +22,8 @@ bool is_kho_boot(void);
>
> int kho_preserve_folio(struct folio *folio);
> void kho_unpreserve_folio(struct folio *folio);
> +int kho_preserve_page(struct page *page, unsigned int order);
> +void kho_unpreserve_page(struct page *page, unsigned int order);
> int kho_preserve_pages(struct page *page, unsigned long nr_pages);
> void kho_unpreserve_pages(struct page *page, unsigned long nr_pages);
> int kho_preserve_vmalloc(void *ptr, struct kho_vmalloc *preservation);
> @@ -30,6 +32,7 @@ void *kho_alloc_preserve(size_t size);
> void kho_unpreserve_free(void *mem);
> void kho_restore_free(void *mem);
> struct folio *kho_restore_folio(phys_addr_t phys);
> +struct page *kho_restore_page(phys_addr_t phys);
> struct page *kho_restore_pages(phys_addr_t phys, unsigned long nr_pages);
> void *kho_restore_vmalloc(const struct kho_vmalloc *preservation);
> int kho_add_subtree(const char *name, void *blob, size_t size);
> @@ -65,6 +68,13 @@ static inline int kho_preserve_pages(struct page *page, unsigned int nr_pages)
>
> static inline void kho_unpreserve_pages(struct page *page, unsigned int nr_pages) { }
>
> +static inline int kho_preserve_page(struct page *page, unsigned int order)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline void kho_unpreserve_page(struct page *page, unsigned int order) { }
> +
> static inline int kho_preserve_vmalloc(void *ptr,
> struct kho_vmalloc *preservation)
> {
> diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
> index 07f6f453cd69d..43ae3e2de5da2 100644
> --- a/kernel/liveupdate/kexec_handover.c
> +++ b/kernel/liveupdate/kexec_handover.c
> @@ -393,7 +393,7 @@ 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)
> {
> struct page *page = pfn_to_online_page(PHYS_PFN(phys));
> unsigned long nr_pages;
> @@ -432,12 +432,44 @@ 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);
>
> return page ? page_folio(page) : NULL;
> }
> EXPORT_SYMBOL_GPL(kho_restore_folio);
>
> +/**
> + * kho_restore_page - restore a higher-order unsplit page block.
> + * @phys: physical address of the first page.
> + *
> + * Restore a higher-order unsplit page block that was preserved with
> + * kho_preserve_page().
> + *
> + * Return: the head page on success, NULL on failure.
> + */
> +struct page *kho_restore_page(phys_addr_t phys)
> +{
> + struct page *page = pfn_to_online_page(PHYS_PFN(phys));
> + unsigned long nr_pages;
> + union kho_page_info info;
> +
> + if (!page)
> + return NULL;
> +
> + info.page_private = page->private;
> + if (WARN_ON_ONCE(info.magic != KHO_PAGE_MAGIC))
> + return NULL;
> +
> + nr_pages = (1UL << info.order);
> + page->private = 0;
> +
> + kho_init_unsplit_pages(page, info.order);
> + adjust_managed_page_count(page, nr_pages);
> +
> + return page;
Hmm, it copies a lot from __kho_init_page().
We should keep the common part of order-0 page/folio/high-order page
initialization in a single place.
--
Sincerely yours,
Mike.