Allow users of KHO to cancel the previous preservation by adding the
necessary interfaces to unpreserve folio and pages.

Signed-off-by: Pasha Tatashin <[email protected]>
---
 include/linux/kexec_handover.h | 12 +++++
 kernel/kexec_handover.c        | 85 ++++++++++++++++++++++++++++------
 2 files changed, 84 insertions(+), 13 deletions(-)

diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h
index 2faf290803ce..4ba145713838 100644
--- a/include/linux/kexec_handover.h
+++ b/include/linux/kexec_handover.h
@@ -43,7 +43,9 @@ 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);
 struct folio *kho_restore_folio(phys_addr_t phys);
 struct page *kho_restore_pages(phys_addr_t phys, unsigned int nr_pages);
@@ -76,11 +78,21 @@ 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)
 {
diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c
index 846fb4aa6948..77fd7dd991d2 100644
--- a/kernel/kexec_handover.c
+++ b/kernel/kexec_handover.c
@@ -153,26 +153,33 @@ static void *xa_load_or_alloc(struct xarray *xa, unsigned 
long index, size_t sz)
        return 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;
+
+       bits = xa_load(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
+       if (!bits)
+               return;
 
-               physxa = xa_load(&track->orders, order);
-               if (!physxa)
-                       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;
 
-               bits = xa_load(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
-               if (!bits)
-                       continue;
+       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;
        }
@@ -734,6 +741,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.
@@ -773,6 +804,34 @@ 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 contigious  pages starting from @page.
+ * This call must exactly match a granularity at which memory was originally
+ * preserved by kho_preserve_pages, call with the same @page and
+ * @nr_pages). 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 *);
 };
-- 
2.51.0.618.g983fd99d29-goog


Reply via email to