+++ /dev/null
-From stable+bounces-289085-greg=kroah.com@vger.kernel.org Sat Jul 25 12:54:43 2026
-From: Sasha Levin <sashal@kernel.org>
-Date: Sat, 25 Jul 2026 06:54:32 -0400
-Subject: kho: add interfaces to unpreserve folios, page ranges, and vmalloc
-To: stable@vger.kernel.org
-Cc: Pasha Tatashin <pasha.tatashin@soleen.com>, Pratyush Yadav <pratyush@kernel.org>, "Mike Rapoport (Microsoft)" <rppt@kernel.org>, Alexander Graf <graf@amazon.com>, Changyuan Lyu <changyuanl@google.com>, Christian Brauner <brauner@kernel.org>, Jason Gunthorpe <jgg@nvidia.com>, Jason Gunthorpe <jgg@ziepe.ca>, Jonathan Corbet <corbet@lwn.net>, Masahiro Yamada <masahiroy@kernel.org>, Miguel Ojeda <ojeda@kernel.org>, Randy Dunlap <rdunlap@infradead.org>, Simon Horman <horms@kernel.org>, Tejun Heo <tj@kernel.org>, Zhu Yanjun <yanjun.zhu@linux.dev>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
-Message-ID: <20260725105433.1853930-1-sashal@kernel.org>
-
-From: Pasha Tatashin <pasha.tatashin@soleen.com>
-
-[ Upstream commit 36f8f7ef7fd2f238922e9d217e86c69838319d8c ]
-
-Allow users of KHO to cancel the previous preservation by adding the
-necessary interfaces to unpreserve folio, pages, and vmallocs.
-
-Link: https://lkml.kernel.org/r/20251101142325.1326536-4-pasha.tatashin@soleen.com
-Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
-Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
-Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
-Cc: Alexander Graf <graf@amazon.com>
-Cc: Changyuan Lyu <changyuanl@google.com>
-Cc: Christian Brauner <brauner@kernel.org>
-Cc: Jason Gunthorpe <jgg@nvidia.com>
-Cc: Jason Gunthorpe <jgg@ziepe.ca>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Masahiro Yamada <masahiroy@kernel.org>
-Cc: Miguel Ojeda <ojeda@kernel.org>
-Cc: Randy Dunlap <rdunlap@infradead.org>
-Cc: Simon Horman <horms@kernel.org>
-Cc: Tejun Heo <tj@kernel.org>
-Cc: Zhu Yanjun <yanjun.zhu@linux.dev>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Stable-dep-of: 0e39380a7316 ("kho: make sure scratch size is always aligned by CMA_MIN_ALIGNMENT_BYTES")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- include/linux/kexec_handover.h | 18 +++++++
- kernel/kexec_handover.c | 104 +++++++++++++++++++++++++++++++++++------
- 2 files changed, 109 insertions(+), 13 deletions(-)
-
---- a/include/linux/kexec_handover.h
-+++ b/include/linux/kexec_handover.h
-@@ -52,8 +52,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);
-@@ -83,16 +86,31 @@ static inline int kho_preserve_folio(str
- 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)
- {
---- a/kernel/kexec_handover.c
-+++ b/kernel/kexec_handover.c
-@@ -153,26 +153,33 @@ static void *xa_load_or_alloc(struct xar
- 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 (WARN_ON_ONCE(!physxa))
-+ return;
-
-- physxa = xa_load(&track->orders, order);
-- if (WARN_ON_ONCE(!physxa))
-- return;
-+ bits = xa_load(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
-+ if (WARN_ON_ONCE(!bits))
-+ return;
-+
-+ 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 (WARN_ON_ONCE(!bits))
-- return;
-+ 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;
- }
-@@ -740,6 +747,30 @@ int kho_preserve_folio(struct folio *fol
- 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.
- * @nr_pages: number of pages.
-@@ -783,6 +814,33 @@ int kho_preserve_pages(struct page *page
- }
- 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 *);
- };
-@@ -945,6 +1003,26 @@ err_free:
- 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.
- * @preservation: preservation metadata.