[RFC PATCH 4/4] kho: Introduce kho_split_preserved_pages() helper

Pranjal Shrivastava <[email protected]>
Newsgroups org.infradead.lists.kexec,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <[email protected]>
A driver may need to split a high-order allocation that has already
been preserved. If the pages are split using split_page() manually,
the refcounts would change but KHO won't record the change in the
preserved page-type, resulting in a metadata mismatch during
restoration in the new kernel.

Introduce kho_split_preserved_pages() to handle splitting of preserved
pages. The helper follows an unpreserve -> split -> re-preserve sequence,
while ensuring that the KHO radix tree is updated with the correct
KHO_PAGE_SPLIT type bits.

The helper returns 0 on success, or a negative error code if the
re-preservation fails. Callers must ensure the provided order matches
the original allocation and that the operation is serialized against
other preservation API calls.

Signed-off-by: Pranjal Shrivastava <[email protected]>
---
 include/linux/kexec_handover.h     |  7 +++++++
 kernel/liveupdate/kexec_handover.c | 23 +++++++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h
index 8968c56d2d73..452e38bb2076 100644
--- a/include/linux/kexec_handover.h
+++ b/include/linux/kexec_handover.h
@@ -24,6 +24,7 @@ int kho_preserve_folio(struct folio *folio);
 void kho_unpreserve_folio(struct folio *folio);
 int kho_preserve_pages(struct page *page, unsigned long nr_pages);
 void kho_unpreserve_pages(struct page *page, unsigned long nr_pages);
+int kho_split_preserved_pages(struct page *page, unsigned int order);
 int kho_preserve_vmalloc(void *ptr, struct kho_vmalloc *preservation);
 void kho_unpreserve_vmalloc(struct kho_vmalloc *preservation);
 void *kho_alloc_preserve(size_t size);
@@ -65,6 +66,12 @@ 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_split_preserved_pages(struct page *page,
+					    unsigned int order)
+{
+	return -EOPNOTSUPP;
+}
+
 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 f6ca5e24c740..ea08248901b5 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -1018,6 +1018,29 @@ void kho_unpreserve_pages(struct page *page, unsigned long nr_pages)
 }
 EXPORT_SYMBOL_GPL(kho_unpreserve_pages);
 
+/**
+ * kho_split_preserved_pages - split contiguous pages that are preserved
+ * @page: first page in the list.
+ * @order: the order of the original allocation.
+ *
+ * This function allows to split a high-order allocation that has been
+ * preserved across kexec. It unpreserves the pages, splits them using
+ * split_page() and then re-preserves them as individual pages.
+ *
+ * This function MUST only be called on pages that are currently preserved.
+ * The @order provided MUST match the order used during the initial
+ * preservation.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int kho_split_preserved_pages(struct page *page, unsigned int order)
+{
+	kho_unpreserve_pages(page, 1UL << order);
+	split_page(page, order);
+	return kho_preserve_pages(page, 1UL << order);
+}
+EXPORT_SYMBOL_GPL(kho_split_preserved_pages);
+
 /* vmalloc flags KHO supports */
 #define KHO_VMALLOC_SUPPORTED_FLAGS	(VM_ALLOC | VM_ALLOW_HUGE_VMAP)
 
-- 
2.55.0.rc0.799.gd6f94ed593-goog
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.