Commit dda9dbfe authored by Hari Bathini's avatar Hari Bathini Committed by Michael Ellerman

powerpc/fadump: consider reserved ranges while releasing memory

Commit 0962e800 ("powerpc/prom: Scan reserved-ranges node for
memory reservations") enabled support to parse 'reserved-ranges' DT
node to reserve kernel memory falling in these ranges for firmware
purposes. Along with the preserved area memory, ensure memory in
reserved ranges is not overlapped with memory released by capture
kernel aftering saving vmcore. Also, fix the off-by-one error in
fadump_release_reserved_area function while releasing memory.
Signed-off-by: default avatarHari Bathini <hbathini@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/156821371358.5656.6061214942558818661.stgit@hbathini.in.ibm.com
parent e4fc48fb
...@@ -36,6 +36,7 @@ static struct fw_dump fw_dump; ...@@ -36,6 +36,7 @@ static struct fw_dump fw_dump;
static DEFINE_MUTEX(fadump_mutex); static DEFINE_MUTEX(fadump_mutex);
struct fadump_mrange_info crash_mrange_info = { "crash", NULL, 0, 0, 0 }; struct fadump_mrange_info crash_mrange_info = { "crash", NULL, 0, 0, 0 };
struct fadump_mrange_info reserved_mrange_info = { "reserved", NULL, 0, 0, 0 };
#ifdef CONFIG_CMA #ifdef CONFIG_CMA
static struct cma *fadump_cma; static struct cma *fadump_cma;
...@@ -1009,41 +1010,142 @@ static void fadump_free_reserved_memory(unsigned long start_pfn, ...@@ -1009,41 +1010,142 @@ static void fadump_free_reserved_memory(unsigned long start_pfn,
/* /*
* Skip memory holes and free memory that was actually reserved. * Skip memory holes and free memory that was actually reserved.
*/ */
static void fadump_release_reserved_area(unsigned long start, unsigned long end) static void fadump_release_reserved_area(u64 start, u64 end)
{ {
u64 tstart, tend, spfn, epfn;
struct memblock_region *reg; struct memblock_region *reg;
unsigned long tstart, tend;
unsigned long start_pfn = PHYS_PFN(start);
unsigned long end_pfn = PHYS_PFN(end);
spfn = PHYS_PFN(start);
epfn = PHYS_PFN(end);
for_each_memblock(memory, reg) { for_each_memblock(memory, reg) {
tstart = max(start_pfn, memblock_region_memory_base_pfn(reg)); tstart = max_t(u64, spfn, memblock_region_memory_base_pfn(reg));
tend = min(end_pfn, memblock_region_memory_end_pfn(reg)); tend = min_t(u64, epfn, memblock_region_memory_end_pfn(reg));
if (tstart < tend) { if (tstart < tend) {
fadump_free_reserved_memory(tstart, tend); fadump_free_reserved_memory(tstart, tend);
if (tend == end_pfn) if (tend == epfn)
break; break;
start_pfn = tend + 1; spfn = tend;
} }
} }
} }
/* /*
* Release the memory that was reserved in early boot to preserve the memory * Sort the mem ranges in-place and merge adjacent ranges
* contents. The released memory will be available for general use. * to minimize the memory ranges count.
*/ */
static void fadump_release_memory(unsigned long begin, unsigned long end) static void sort_and_merge_mem_ranges(struct fadump_mrange_info *mrange_info)
{ {
unsigned long ra_start, ra_end; struct fadump_memory_range *mem_ranges;
struct fadump_memory_range tmp_range;
u64 base, size;
int i, j, idx;
if (!reserved_mrange_info.mem_range_cnt)
return;
/* Sort the memory ranges */
mem_ranges = mrange_info->mem_ranges;
for (i = 0; i < mrange_info->mem_range_cnt; i++) {
idx = i;
for (j = (i + 1); j < mrange_info->mem_range_cnt; j++) {
if (mem_ranges[idx].base > mem_ranges[j].base)
idx = j;
}
if (idx != i) {
tmp_range = mem_ranges[idx];
mem_ranges[idx] = mem_ranges[i];
mem_ranges[i] = tmp_range;
}
}
/* Merge adjacent reserved ranges */
idx = 0;
for (i = 1; i < mrange_info->mem_range_cnt; i++) {
base = mem_ranges[i-1].base;
size = mem_ranges[i-1].size;
if (mem_ranges[i].base == (base + size))
mem_ranges[idx].size += mem_ranges[i].size;
else {
idx++;
if (i == idx)
continue;
mem_ranges[idx] = mem_ranges[i];
}
}
mrange_info->mem_range_cnt = idx + 1;
}
/*
* Scan reserved-ranges to consider them while reserving/releasing
* memory for FADump.
*/
static inline int fadump_scan_reserved_mem_ranges(void)
{
struct device_node *root;
const __be32 *prop;
int len, ret = -1;
unsigned long i;
root = of_find_node_by_path("/");
if (!root)
return ret;
prop = of_get_property(root, "reserved-ranges", &len);
if (!prop)
return ret;
/*
* Each reserved range is an (address,size) pair, 2 cells each,
* totalling 4 cells per range.
*/
for (i = 0; i < len / (sizeof(*prop) * 4); i++) {
u64 base, size;
base = of_read_number(prop + (i * 4) + 0, 2);
size = of_read_number(prop + (i * 4) + 2, 2);
if (size) {
ret = fadump_add_mem_range(&reserved_mrange_info,
base, base + size);
if (ret < 0) {
pr_warn("some reserved ranges are ignored!\n");
break;
}
}
}
return ret;
}
/*
* Release the memory that was reserved during early boot to preserve the
* crash'ed kernel's memory contents except reserved dump area (permanent
* reservation) and reserved ranges used by F/W. The released memory will
* be available for general use.
*/
static void fadump_release_memory(u64 begin, u64 end)
{
u64 ra_start, ra_end, tstart;
int i, ret;
fadump_scan_reserved_mem_ranges();
ra_start = fw_dump.reserve_dump_area_start; ra_start = fw_dump.reserve_dump_area_start;
ra_end = ra_start + fw_dump.reserve_dump_area_size; ra_end = ra_start + fw_dump.reserve_dump_area_size;
/* /*
* exclude the dump reserve area. Will reuse it for next * Add reserved dump area to reserved ranges list
* fadump registration. * and exclude all these ranges while releasing memory.
*/
ret = fadump_add_mem_range(&reserved_mrange_info, ra_start, ra_end);
if (ret != 0) {
/*
* Not enough memory to setup reserved ranges but the system is
* running shortage of memory. So, release all the memory except
* Reserved dump area (reused for next fadump registration).
*/ */
if (begin < ra_end && end > ra_start) { if (begin < ra_end && end > ra_start) {
if (begin < ra_start) if (begin < ra_start)
...@@ -1052,6 +1154,29 @@ static void fadump_release_memory(unsigned long begin, unsigned long end) ...@@ -1052,6 +1154,29 @@ static void fadump_release_memory(unsigned long begin, unsigned long end)
fadump_release_reserved_area(ra_end, end); fadump_release_reserved_area(ra_end, end);
} else } else
fadump_release_reserved_area(begin, end); fadump_release_reserved_area(begin, end);
return;
}
/* Get the reserved ranges list in order first. */
sort_and_merge_mem_ranges(&reserved_mrange_info);
/* Exclude reserved ranges and release remaining memory */
tstart = begin;
for (i = 0; i < reserved_mrange_info.mem_range_cnt; i++) {
ra_start = reserved_mrange_info.mem_ranges[i].base;
ra_end = ra_start + reserved_mrange_info.mem_ranges[i].size;
if (tstart >= ra_end)
continue;
if (tstart < ra_start)
fadump_release_reserved_area(tstart, ra_start);
tstart = ra_end;
}
if (tstart < end)
fadump_release_reserved_area(tstart, end);
} }
static void fadump_invalidate_release_mem(void) static void fadump_invalidate_release_mem(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment