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)