[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251101142325.1326536-4-pasha.tatashin@soleen.com>
Date: Sat, 1 Nov 2025 10:23:19 -0400
From: Pasha Tatashin <pasha.tatashin@...een.com>
To: akpm@...ux-foundation.org,
brauner@...nel.org,
corbet@....net,
graf@...zon.com,
jgg@...pe.ca,
linux-kernel@...r.kernel.org,
linux-kselftest@...r.kernel.org,
linux-mm@...ck.org,
masahiroy@...nel.org,
ojeda@...nel.org,
pasha.tatashin@...een.com,
pratyush@...nel.org,
rdunlap@...radead.org,
rppt@...nel.org,
tj@...nel.org,
yanjun.zhu@...ux.dev
Subject: [PATCH v9 3/9] kho: add interfaces to unpreserve folios, page ranges, and vmalloc
Allow users of KHO to cancel the previous preservation by adding the
necessary interfaces to unpreserve folio, pages, and vmallocs.
Signed-off-by: Pasha Tatashin <pasha.tatashin@...een.com>
Reviewed-by: Pratyush Yadav <pratyush@...nel.org>
Reviewed-by: Mike Rapoport (Microsoft) <rppt@...nel.org>
---
include/linux/kexec_handover.h | 18 ++++++
kernel/kexec_handover.c | 104 ++++++++++++++++++++++++++++-----
2 files changed, 109 insertions(+), 13 deletions(-)
diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h
index 0d860d793b66..80ece4232617 100644
--- a/include/linux/kexec_handover.h
+++ b/include/linux/kexec_handover.h
@@ -43,8 +43,11 @@ bool kho_is_enabled(void);
bool is_kho_boot(void);
int kho_preserve_folio(struct folio *folio);
+int kho_unpreserve_folio(struct folio *folio);
int kho_preserve_pages(struct page *page, unsigned int nr_pages);
+int kho_unpreserve_pages(struct page *page, unsigned int nr_pages);
int kho_preserve_vmalloc(void *ptr, struct kho_vmalloc *preservation);
+int kho_unpreserve_vmalloc(struct kho_vmalloc *preservation);
struct folio *kho_restore_folio(phys_addr_t phys);
struct page *kho_restore_pages(phys_addr_t phys, unsigned int nr_pages);
void *kho_restore_vmalloc(const struct kho_vmalloc *preservation);
@@ -72,17 +75,32 @@ static inline int kho_preserve_folio(struct folio *folio)
return -EOPNOTSUPP;
}
+static inline int kho_unpreserve_folio(struct folio *folio)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int kho_preserve_pages(struct page *page, unsigned int nr_pages)
{
return -EOPNOTSUPP;
}
+static inline int kho_unpreserve_pages(struct page *page, unsigned int nr_pages)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int kho_preserve_vmalloc(void *ptr,
struct kho_vmalloc *preservation)
{
return -EOPNOTSUPP;
}
+static inline int kho_unpreserve_vmalloc(struct kho_vmalloc *preservation)
+{
+ return -EOPNOTSUPP;
+}
+
static inline struct folio *kho_restore_folio(phys_addr_t phys)
{
return NULL;
diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c
index 82137eba1474..c514b300ebe6 100644
--- a/kernel/kexec_handover.c
+++ b/kernel/kexec_handover.c
@@ -157,26 +157,33 @@ static void *xa_load_or_alloc(struct xarray *xa, unsigned long index)
return no_free_ptr(elm);
}
-static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn,
- unsigned long end_pfn)
+static void __kho_unpreserve_order(struct kho_mem_track *track, unsigned long pfn,
+ unsigned int order)
{
struct kho_mem_phys_bits *bits;
struct kho_mem_phys *physxa;
+ const unsigned long pfn_high = pfn >> order;
- while (pfn < end_pfn) {
- const unsigned int order =
- min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn));
- const unsigned long pfn_high = pfn >> order;
+ physxa = xa_load(&track->orders, order);
+ if (!physxa)
+ return;
- physxa = xa_load(&track->orders, order);
- if (!physxa)
- continue;
+ bits = xa_load(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
+ if (!bits)
+ return;
- bits = xa_load(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
- if (!bits)
- continue;
+ clear_bit(pfn_high % PRESERVE_BITS, bits->preserve);
+}
+
+static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn,
+ unsigned long end_pfn)
+{
+ unsigned int order;
+
+ while (pfn < end_pfn) {
+ order = min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn));
- clear_bit(pfn_high % PRESERVE_BITS, bits->preserve);
+ __kho_unpreserve_order(track, pfn, order);
pfn += 1 << order;
}
@@ -745,6 +752,30 @@ int kho_preserve_folio(struct folio *folio)
}
EXPORT_SYMBOL_GPL(kho_preserve_folio);
+/**
+ * kho_unpreserve_folio - unpreserve a folio.
+ * @folio: folio to unpreserve.
+ *
+ * Instructs KHO to unpreserve a folio that was preserved by
+ * kho_preserve_folio() before. The provided @folio (pfn and order)
+ * must exactly match a previously preserved folio.
+ *
+ * Return: 0 on success, error code on failure
+ */
+int kho_unpreserve_folio(struct folio *folio)
+{
+ const unsigned long pfn = folio_pfn(folio);
+ const unsigned int order = folio_order(folio);
+ struct kho_mem_track *track = &kho_out.track;
+
+ if (kho_out.finalized)
+ return -EBUSY;
+
+ __kho_unpreserve_order(track, pfn, order);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kho_unpreserve_folio);
+
/**
* kho_preserve_pages - preserve contiguous pages across kexec
* @page: first page in the list.
@@ -789,6 +820,33 @@ int kho_preserve_pages(struct page *page, unsigned int nr_pages)
}
EXPORT_SYMBOL_GPL(kho_preserve_pages);
+/**
+ * kho_unpreserve_pages - unpreserve contiguous pages.
+ * @page: first page in the list.
+ * @nr_pages: number of pages.
+ *
+ * Instructs KHO to unpreserve @nr_pages contiguous pages starting from @page.
+ * This must be called with the same @page and @nr_pages as the corresponding
+ * kho_preserve_pages() call. Unpreserving arbitrary sub-ranges of larger
+ * preserved blocks is not supported.
+ *
+ * Return: 0 on success, error code on failure
+ */
+int kho_unpreserve_pages(struct page *page, unsigned int nr_pages)
+{
+ struct kho_mem_track *track = &kho_out.track;
+ const unsigned long start_pfn = page_to_pfn(page);
+ const unsigned long end_pfn = start_pfn + nr_pages;
+
+ if (kho_out.finalized)
+ return -EBUSY;
+
+ __kho_unpreserve(track, start_pfn, end_pfn);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kho_unpreserve_pages);
+
struct kho_vmalloc_hdr {
DECLARE_KHOSER_PTR(next, struct kho_vmalloc_chunk *);
};
@@ -949,6 +1007,26 @@ int kho_preserve_vmalloc(void *ptr, struct kho_vmalloc *preservation)
}
EXPORT_SYMBOL_GPL(kho_preserve_vmalloc);
+/**
+ * kho_unpreserve_vmalloc - unpreserve memory allocated with vmalloc()
+ * @preservation: preservation metadata returned by kho_preserve_vmalloc()
+ *
+ * Instructs KHO to unpreserve the area in vmalloc address space that was
+ * previously preserved with kho_preserve_vmalloc().
+ *
+ * Return: 0 on success, error code on failure
+ */
+int kho_unpreserve_vmalloc(struct kho_vmalloc *preservation)
+{
+ if (kho_out.finalized)
+ return -EBUSY;
+
+ kho_vmalloc_free_chunks(preservation);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kho_unpreserve_vmalloc);
+
/**
* kho_restore_vmalloc - recreates and populates an area in vmalloc address
* space from the preserved memory.
--
2.51.1.930.gacf6e81ea2-goog
Powered by blists - more mailing lists