Around 95% of memory is reserved by fadump/capture kernel. All this
memory is freed, one page at a time, on writing '1' to the node
/sys/kernel/fadump_release_mem. On systems with large memory, this
can take a long time to complete, leading to soft lockup warning
messages. To avoid this, add reschedule points at regular intervals.

Suggested-by: Michael Ellerman <m...@ellerman.id.au>
Signed-off-by: Hari Bathini <hbath...@linux.vnet.ibm.com>
---
 arch/powerpc/kernel/fadump.c |   60 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 49 insertions(+), 11 deletions(-)

diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
index 466569e..0babefc 100644
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
@@ -1046,28 +1046,66 @@ void fadump_cleanup(void)
        }
 }
 
+/* Time to wait before a reschedule point */
+#define RELEASE_TIME_LIMIT                     500     /* in milliseconds */
+
+/* Release memory in batches of 'N' pages each */
+#define RELEASE_PAGES_BATCH                    (1 << 22)
+
+static void fadump_free_reserved_memory(unsigned long start, unsigned long end)
+{
+       unsigned long pfn, start_pfn, end_pfn;
+       unsigned int remaining = end > start ? (end - start) >> PAGE_SHIFT : 0;
+       unsigned long time_limit = jiffies +
+                                       msecs_to_jiffies(RELEASE_TIME_LIMIT);
+
+       while (remaining) {
+               /* A reschedule point for every 'X' milliseconds */
+               if (time_after_eq(jiffies, time_limit)) {
+                       cond_resched();
+                       time_limit = jiffies +
+                                       msecs_to_jiffies(RELEASE_TIME_LIMIT);
+               }
+
+               /* release memory in batches of 'N' pages */
+               start_pfn = start >> PAGE_SHIFT;
+               if (remaining > RELEASE_PAGES_BATCH) {
+                       end_pfn = start_pfn + RELEASE_PAGES_BATCH;
+                       remaining -= RELEASE_PAGES_BATCH;
+               } else {
+                       end_pfn = end >> PAGE_SHIFT;
+                       remaining = 0;
+               }
+
+               for (pfn = start_pfn; pfn < end_pfn; pfn++)
+                       free_reserved_page(pfn_to_page(pfn));
+
+               start = end_pfn << PAGE_SHIFT;
+       }
+}
+
 /*
  * Release the memory that was reserved in early boot to preserve the memory
  * contents. The released memory will be available for general use.
  */
 static void fadump_release_memory(unsigned long begin, unsigned long end)
 {
-       unsigned long addr;
        unsigned long ra_start, ra_end;
 
        ra_start = fw_dump.reserve_dump_area_start;
        ra_end = ra_start + fw_dump.reserve_dump_area_size;
 
-       for (addr = begin; addr < end; addr += PAGE_SIZE) {
-               /*
-                * exclude the dump reserve area. Will reuse it for next
-                * fadump registration.
-                */
-               if (addr <= ra_end && ((addr + PAGE_SIZE) > ra_start))
-                       continue;
-
-               free_reserved_page(pfn_to_page(addr >> PAGE_SHIFT));
-       }
+       /*
+        * exclude the dump reserve area. Will reuse it for next
+        * fadump registration.
+        */
+       if (begin < ra_end && end > ra_start) {
+               if (begin < ra_start)
+                       fadump_free_reserved_memory(begin, ra_start);
+               if (end > ra_end)
+                       fadump_free_reserved_memory(ra_end, end);
+       } else
+               fadump_free_reserved_memory(begin, end);
 }
 
 static void fadump_invalidate_release_mem(void)

Reply via email to