diff mbox series

[v4,1/7] powerpc/fadump: Move the metadata region to start of the reserved area.

Message ID 152420065839.31037.9373191008433546810.stgit@jupiter.in.ibm.com (mailing list archive)
State Superseded
Headers show
Series powerpc/fadump: Improvements and fixes for firmware-assisted dump. | expand

Commit Message

Mahesh J Salgaonkar April 20, 2018, 5:04 a.m. UTC
From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

Currently the metadata region that holds crash info structure and ELF core
header is placed towards the end of reserved memory area. This patch places
it at the beginning of the reserved memory area. It also introduces
additional dump section called metadata section to communicate location
of metadata region to 2nd kernel. This patch also maintains the
compatibility between production/capture kernels irrespective of their
kernel versions. Both combination older/newer and newer/older works fine.

Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/fadump.h |    9 ++
 arch/powerpc/kernel/fadump.c      |  177 ++++++++++++++++++++++++++++++++-----
 2 files changed, 160 insertions(+), 26 deletions(-)

Comments

Nicholas Piggin April 22, 2018, 1:58 a.m. UTC | #1
On Fri, 20 Apr 2018 10:34:18 +0530
Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:

> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> 
> Currently the metadata region that holds crash info structure and ELF core
> header is placed towards the end of reserved memory area. This patch places
> it at the beginning of the reserved memory area. It also introduces
> additional dump section called metadata section to communicate location
> of metadata region to 2nd kernel. This patch also maintains the
> compatibility between production/capture kernels irrespective of their
> kernel versions. Both combination older/newer and newer/older works fine.

Trying to look at the patches it might help me if you document reasons
for why this change is made changelog, even if it may be obvious to
someone who knows the code better.

I thought you could include the documentation change in this patch as
well, but maybe that's a matter of preference.

Thanks,
Nick
Mahesh J Salgaonkar April 23, 2018, 5:16 a.m. UTC | #2
On 04/22/2018 07:28 AM, Nicholas Piggin wrote:
> On Fri, 20 Apr 2018 10:34:18 +0530
> Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
> 
>> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
>>
>> Currently the metadata region that holds crash info structure and ELF core
>> header is placed towards the end of reserved memory area. This patch places
>> it at the beginning of the reserved memory area. It also introduces
>> additional dump section called metadata section to communicate location
>> of metadata region to 2nd kernel. This patch also maintains the
>> compatibility between production/capture kernels irrespective of their
>> kernel versions. Both combination older/newer and newer/older works fine.
> 
> Trying to look at the patches it might help me if you document reasons
> for why this change is made changelog, even if it may be obvious to
> someone who knows the code better.

Yeah, I should have mentioned that this patch provides the foundation
for CMA patch 4. With CMA reservation we now allocate metadata region
using cma_alloc() which always allocates metadata region at the start of
CMA reserved region. Earlier in v1, I had this change included along
with CMA reservation patch. But then to make things simpler for review I
did a logical split of movement of metadata region and CMA reservation
patch separately. I think I should order patch 1, 2 and 4 in a sequence
and Move patch3 to patch 1.

> 
> I thought you could include the documentation change in this patch as
> well, but maybe that's a matter of preference.

Yeah that's how I prefer it :-), but that just me. But if it helps in
review I can fold it into 1.

Thanks,
-Mahesh.
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h
index 5a23010af600..03d5140a6f0d 100644
--- a/arch/powerpc/include/asm/fadump.h
+++ b/arch/powerpc/include/asm/fadump.h
@@ -53,6 +53,11 @@ 
 #define FADUMP_HPTE_REGION	0x0002
 #define FADUMP_REAL_MODE_REGION	0x0011
 
+/* User define dump sections.
+ * Per PAPR: 0x0100 - 0xFFFF OS defined source types
+ */
+#define FADUMP_METADATA_REGION	0x0100
+
 /* Dump request flag */
 #define FADUMP_REQUEST_FLAG	0x00000001
 
@@ -61,6 +66,9 @@ 
 #define FADUMP_UNREGISTER	2
 #define FADUMP_INVALIDATE	3
 
+/* Number of dump sections requested by kernel */
+#define FADUMP_NUM_SECTIONS	4
+
 /* Dump status flag */
 #define FADUMP_ERROR_FLAG	0x2000
 
@@ -119,6 +127,7 @@  struct fadump_mem_struct {
 	struct fadump_section		cpu_state_data;
 	struct fadump_section		hpte_region;
 	struct fadump_section		rmr_region;
+	struct fadump_section		metadata_region;
 };
 
 /* Firmware-assisted dump configuration details. */
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
index 8ceabef40d3d..43bfa535d0ea 100644
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
@@ -188,17 +188,40 @@  static void fadump_show_config(void)
 	pr_debug("Boot memory size  : %lx\n", fw_dump.boot_memory_size);
 }
 
+static unsigned long get_fadump_metadata_size(void)
+{
+	unsigned long size = 0;
+
+	size += sizeof(struct fadump_crash_info_header);
+	size += sizeof(struct elfhdr); /* ELF core header.*/
+	size += sizeof(struct elf_phdr); /* place holder for cpu notes */
+	/* Program headers for crash memory regions. */
+	size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);
+
+	size = PAGE_ALIGN(size);
+	pr_debug("fadump Metadata size is %ld\n", size);
+	return size;
+}
+
 static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
 				unsigned long addr)
 {
+	uint16_t num_sections = 0;
+	unsigned long metadata_base = 0;
+	unsigned long metadata_size = 0;
+
 	if (!fdm)
 		return 0;
 
+	/* Skip the fadump metadata area. */
+	metadata_base = addr;
+	metadata_size = get_fadump_metadata_size();
+	addr += metadata_size;
+
 	memset(fdm, 0, sizeof(struct fadump_mem_struct));
 	addr = addr & PAGE_MASK;
 
 	fdm->header.dump_format_version = cpu_to_be32(0x00000001);
-	fdm->header.dump_num_sections = cpu_to_be16(3);
 	fdm->header.dump_status_flag = 0;
 	fdm->header.offset_first_dump_section =
 		cpu_to_be32((u32)offsetof(struct fadump_mem_struct, cpu_state_data));
@@ -222,6 +245,7 @@  static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
 	fdm->cpu_state_data.source_address = 0;
 	fdm->cpu_state_data.source_len = cpu_to_be64(fw_dump.cpu_state_data_size);
 	fdm->cpu_state_data.destination_address = cpu_to_be64(addr);
+	num_sections++;
 	addr += fw_dump.cpu_state_data_size;
 
 	/* hpte region section */
@@ -230,6 +254,7 @@  static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
 	fdm->hpte_region.source_address = 0;
 	fdm->hpte_region.source_len = cpu_to_be64(fw_dump.hpte_region_size);
 	fdm->hpte_region.destination_address = cpu_to_be64(addr);
+	num_sections++;
 	addr += fw_dump.hpte_region_size;
 
 	/* RMA region section */
@@ -238,8 +263,27 @@  static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
 	fdm->rmr_region.source_address = cpu_to_be64(RMA_START);
 	fdm->rmr_region.source_len = cpu_to_be64(fw_dump.boot_memory_size);
 	fdm->rmr_region.destination_address = cpu_to_be64(addr);
+	num_sections++;
 	addr += fw_dump.boot_memory_size;
 
+	/*
+	 * fadump metadata section.
+	 * This section will help 2nd kernel to locate the metadata base
+	 * looking at source_address. This entry will also copy the metadata
+	 * content after the RMR region dump data, so that old kernels that
+	 * does not support this section can still find the metadata to
+	 * process fadump.
+	 */
+	fdm->metadata_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+	fdm->metadata_region.source_data_type =
+					cpu_to_be16(FADUMP_METADATA_REGION);
+	fdm->metadata_region.source_address = cpu_to_be64(metadata_base);
+	fdm->metadata_region.source_len = cpu_to_be64(metadata_size);
+	fdm->metadata_region.destination_address = cpu_to_be64(addr);
+	num_sections++;
+
+	fdm->header.dump_num_sections = cpu_to_be16(num_sections);
+	BUILD_BUG_ON(num_sections != FADUMP_NUM_SECTIONS);
 	return addr;
 }
 
@@ -325,12 +369,12 @@  static unsigned long get_fadump_area_size(void)
 	size += fw_dump.cpu_state_data_size;
 	size += fw_dump.hpte_region_size;
 	size += fw_dump.boot_memory_size;
-	size += sizeof(struct fadump_crash_info_header);
-	size += sizeof(struct elfhdr); /* ELF core header.*/
-	size += sizeof(struct elf_phdr); /* place holder for cpu notes */
-	/* Program headers for crash memory regions. */
-	size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);
-
+	size += get_fadump_metadata_size();
+	/* Add it twice. We will ask platform to move metadata contents
+	 * at rmr_region.destination_address + source_len, so that old
+	 * kernels also can get metadata to process fadump successfully.
+	 */
+	size += get_fadump_metadata_size();
 	size = PAGE_ALIGN(size);
 	return size;
 }
@@ -355,6 +399,35 @@  static void __init fadump_reserve_crash_area(unsigned long base,
 	}
 }
 
+static inline unsigned long get_fadump_metadata_base(
+			const struct fadump_mem_struct *fdm)
+{
+	const struct fadump_section *dump_section;
+	uint16_t num_sections;
+
+	/* Scan through all dump sections to find out metadata section. */
+	dump_section = &fdm->cpu_state_data;
+	num_sections = be16_to_cpu(fdm->header.dump_num_sections);
+
+	while (num_sections--) {
+		uint16_t type = be16_to_cpu(dump_section->source_data_type);
+		if (type == FADUMP_METADATA_REGION)
+			return be64_to_cpu(dump_section->source_address);
+
+		dump_section++;
+	}
+
+	/*
+	 * Support the older kernel which does not contain metadata
+	 * dump section. We end up here only when fadump is active and
+	 * the previously crashed kernel is old kernel which had metadata
+	 * section at the top of the reserved memory.
+	 */
+	BUG_ON(fw_dump.dump_active == 0);
+	return be64_to_cpu(fdm->rmr_region.destination_address) +
+				be64_to_cpu(fdm->rmr_region.source_len);
+}
+
 int __init fadump_reserve_mem(void)
 {
 	unsigned long base, size, memory_boundary;
@@ -420,9 +493,9 @@  int __init fadump_reserve_mem(void)
 		size = memory_boundary - base;
 		fadump_reserve_crash_area(base, size);
 
-		fw_dump.fadumphdr_addr =
-				be64_to_cpu(fdm_active->rmr_region.destination_address) +
-				be64_to_cpu(fdm_active->rmr_region.source_len);
+		pr_info("Number of kernel Dump sections: %d\n",
+			be16_to_cpu(fdm_active->header.dump_num_sections));
+		fw_dump.fadumphdr_addr = get_fadump_metadata_base(fdm_active);
 		pr_debug("fadumphdr_addr = %p\n",
 				(void *) fw_dump.fadumphdr_addr);
 	} else {
@@ -821,6 +894,38 @@  static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
 
 }
 
+static int validate_dump(const struct fadump_mem_struct *fdm_active)
+{
+	const struct fadump_section *dump_section;
+	uint16_t num_sections = 0;
+	int rc = 0;
+
+	num_sections = be16_to_cpu(fdm_active->header.dump_num_sections);
+	dump_section = &fdm_active->cpu_state_data;
+
+	if ((be16_to_cpu(fdm_active->header.dump_status_flag)
+						== FADUMP_ERROR_FLAG)) {
+		pr_err("Error occured while taking a dump by platform\n");
+		rc = -EINVAL;
+	}
+
+	/* Print Section type and error flags for failed dump section. */
+	while (num_sections--) {
+		if (dump_section->error_flags != 0 ||
+		(dump_section->source_len != dump_section->bytes_dumped)) {
+			pr_err("Section: %04x, Error flag: %04x, "
+				"Bytes Req: %llx, Bytes Dumped: %llx\n",
+				be16_to_cpu(dump_section->source_data_type),
+				be16_to_cpu(dump_section->error_flags),
+				be64_to_cpu(dump_section->source_len),
+				be64_to_cpu(dump_section->bytes_dumped));
+			rc = -EINVAL;
+		}
+		dump_section++;
+	}
+	return rc;
+}
+
 /*
  * Validate and process the dump data stored by firmware before exporting
  * it through '/proc/vmcore'.
@@ -834,17 +939,10 @@  static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
 		return -EINVAL;
 
 	/* Check if the dump data is valid. */
-	if ((be16_to_cpu(fdm_active->header.dump_status_flag) == FADUMP_ERROR_FLAG) ||
-			(fdm_active->cpu_state_data.error_flags != 0) ||
-			(fdm_active->rmr_region.error_flags != 0)) {
+	rc = validate_dump(fdm_active);
+	if (rc) {
 		printk(KERN_ERR "Dump taken by platform is not valid\n");
-		return -EINVAL;
-	}
-	if ((fdm_active->rmr_region.bytes_dumped !=
-			fdm_active->rmr_region.source_len) ||
-			!fdm_active->cpu_state_data.bytes_dumped) {
-		printk(KERN_ERR "Dump taken by platform is incomplete\n");
-		return -EINVAL;
+		return rc;
 	}
 
 	/* Validate the fadump crash info header */
@@ -1107,7 +1205,7 @@  static int register_fadump(void)
 
 	fadump_setup_crash_memory_ranges();
 
-	addr = be64_to_cpu(fdm.rmr_region.destination_address) + be64_to_cpu(fdm.rmr_region.source_len);
+	addr = be64_to_cpu(fdm.metadata_region.source_address);
 	/* Initialize fadump crash info header. */
 	addr = init_fadump_header(addr);
 	vaddr = __va(addr);
@@ -1146,7 +1244,7 @@  static int fadump_unregister_dump(struct fadump_mem_struct *fdm)
 	return 0;
 }
 
-static int fadump_invalidate_dump(struct fadump_mem_struct *fdm)
+static int fadump_invalidate_dump(const struct fadump_mem_struct *fdm)
 {
 	int rc = 0;
 	unsigned int wait_time;
@@ -1177,9 +1275,7 @@  void fadump_cleanup(void)
 {
 	/* Invalidate the registration only if dump is active. */
 	if (fw_dump.dump_active) {
-		init_fadump_mem_struct(&fdm,
-			be64_to_cpu(fdm_active->cpu_state_data.destination_address));
-		fadump_invalidate_dump(&fdm);
+		fadump_invalidate_dump(fdm_active);
 	}
 }
 
@@ -1250,6 +1346,35 @@  static void fadump_release_memory(unsigned long begin, unsigned long end)
 		fadump_release_reserved_area(begin, end);
 }
 
+static inline unsigned long get_fadump_prev_mstart(
+			const struct fadump_mem_struct *fdm)
+{
+	const struct fadump_section *dump_section;
+	uint16_t num_sections;
+
+	/* This function is called only when fadump is active. */
+	BUG_ON(fw_dump.dump_active == 0);
+
+	/* Scan through all dump sections to find out metadata section. */
+	dump_section = &fdm->cpu_state_data;
+	num_sections = be16_to_cpu(fdm->header.dump_num_sections);
+
+	while (num_sections--) {
+		uint16_t type = be16_to_cpu(dump_section->source_data_type);
+		if (type == FADUMP_METADATA_REGION)
+			return be64_to_cpu(dump_section->source_address);
+
+		dump_section++;
+	}
+
+	/*
+	 * Support the older kernel which does not contain metadata
+	 * dump section. For older kernel reserved memory start is
+	 * at cpu_state_data.destination_address.
+	 */
+	return be64_to_cpu(fdm->cpu_state_data.destination_address);
+}
+
 static void fadump_invalidate_release_mem(void)
 {
 	unsigned long reserved_area_start, reserved_area_end;
@@ -1261,7 +1386,7 @@  static void fadump_invalidate_release_mem(void)
 		return;
 	}
 
-	destination_address = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
+	destination_address = get_fadump_prev_mstart(fdm_active);
 	fadump_cleanup();
 	mutex_unlock(&fadump_mutex);