kimage_free_cma() relies on image->nr_segments to iterate over segments.
When an architecture loader (e.g., arm64) truncates nr_segments on a
mid-way failure, CMA pages allocated beyond the new boundary become
unreachable, causing silent memory leaks.

Extract the per-segment freeing logic into the exported helper
kexec_free_segment_cma(), so that architecture loaders can release
individual segments before nr_segments is truncated. Refactor
kimage_free_cma() to loop over the new helper, preserving existing
behavior.

Cc: Andrew Morton <[email protected]>
Cc: Baoquan He <[email protected]>
Cc: Mike Rapoport <[email protected]>
Cc: Pasha Tatashin <[email protected]>
Cc: Pratyush Yadav <[email protected]>
Signed-off-by: Jinjie Ruan <[email protected]>
---
 include/linux/kexec.h |  2 ++
 kernel/kexec_core.c   | 27 +++++++++++++++------------
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 83c296c0eb6c..d7c83938dfd3 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -533,6 +533,7 @@ extern bool kexec_file_dbg_print;
 
 extern void *kimage_map_segment(struct kimage *image, int idx);
 extern void kimage_unmap_segment(void *buffer);
+extern void kexec_free_segment_cma(struct kimage *image, unsigned long idx);
 #else /* !CONFIG_KEXEC_CORE */
 struct pt_regs;
 struct task_struct;
@@ -544,6 +545,7 @@ static inline int kexec_crash_loaded(void) { return 0; }
 static inline void *kimage_map_segment(struct kimage *image, int idx)
 { return NULL; }
 static inline void kimage_unmap_segment(void *buffer) { }
+static inline void kexec_free_segment_cma(struct kimage *image, unsigned long 
idx) { }
 #define kexec_in_progress false
 #endif /* CONFIG_KEXEC_CORE */
 
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 611b15bb1369..ee66e8ffd358 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -554,23 +554,26 @@ static void kimage_free_entry(kimage_entry_t entry)
        kimage_free_pages(page);
 }
 
-static void kimage_free_cma(struct kimage *image)
+void kexec_free_segment_cma(struct kimage *image, unsigned long idx)
 {
-       unsigned long i;
+       unsigned int nr_pages = image->segment_cma_pages[idx];
+       struct page *cma = image->segment_cma[idx];
 
-       for (i = 0; i < image->nr_segments; i++) {
-               struct page *cma = image->segment_cma[i];
-               unsigned int nr_pages = image->segment_cma_pages[i];
+       if (!cma)
+               return;
 
-               if (!cma)
-                       continue;
+       arch_kexec_pre_free_pages(page_address(cma), nr_pages);
+       dma_release_from_contiguous(NULL, cma, nr_pages);
+       image->segment_cma[idx] = NULL;
+       image->segment_cma_pages[idx] = 0;
+}
 
-               arch_kexec_pre_free_pages(page_address(cma), nr_pages);
-               dma_release_from_contiguous(NULL, cma, nr_pages);
-               image->segment_cma[i] = NULL;
-               image->segment_cma_pages[i] = 0;
-       }
+static void kimage_free_cma(struct kimage *image)
+{
+       unsigned long i;
 
+       for (i = 0; i < image->nr_segments; i++)
+               kexec_free_segment_cma(image, i);
 }
 
 void kimage_free(struct kimage *image)
-- 
2.34.1


Reply via email to