Jelajahi Sumber

pstore/ram: Add ftrace messages handling

The ftrace log size is configurable via ramoops.ftrace_size
module option, and the log itself is available via
<pstore-mount>/ftrace-ramoops file.

Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Anton Vorontsov 13 tahun lalu
induk
melakukan
a694d1b591
3 mengubah file dengan 59 tambahan dan 4 penghapusan
  1. 25 0
      Documentation/ramoops.txt
  2. 33 4
      fs/pstore/ram.c
  3. 1 0
      include/linux/pstore_ram.h

+ 25 - 0
Documentation/ramoops.txt

@@ -94,3 +94,28 @@ timestamp and a new line. The dump then continues with the actual data.
 The dump data can be read from the pstore filesystem. The format for these
 The dump data can be read from the pstore filesystem. The format for these
 files is "dmesg-ramoops-N", where N is the record number in memory. To delete
 files is "dmesg-ramoops-N", where N is the record number in memory. To delete
 a stored record from RAM, simply unlink the respective pstore file.
 a stored record from RAM, simply unlink the respective pstore file.
+
+5. Persistent function tracing
+
+Persistent function tracing might be useful for debugging software or hardware
+related hangs. The functions call chain log is stored in a "ftrace-ramoops"
+file. Here is an example of usage:
+
+ # mount -t debugfs debugfs /sys/kernel/debug/
+ # cd /sys/kernel/debug/tracing
+ # echo function > current_tracer
+ # echo 1 > options/func_pstore
+ # reboot -f
+ [...]
+ # mount -t pstore pstore /mnt/
+ # tail /mnt/ftrace-ramoops
+ 0 ffffffff8101ea64  ffffffff8101bcda  native_apic_mem_read <- disconnect_bsp_APIC+0x6a/0xc0
+ 0 ffffffff8101ea44  ffffffff8101bcf6  native_apic_mem_write <- disconnect_bsp_APIC+0x86/0xc0
+ 0 ffffffff81020084  ffffffff8101a4b5  hpet_disable <- native_machine_shutdown+0x75/0x90
+ 0 ffffffff81005f94  ffffffff8101a4bb  iommu_shutdown_noop <- native_machine_shutdown+0x7b/0x90
+ 0 ffffffff8101a6a1  ffffffff8101a437  native_machine_emergency_restart <- native_machine_restart+0x37/0x40
+ 0 ffffffff811f9876  ffffffff8101a73a  acpi_reboot <- native_machine_emergency_restart+0xaa/0x1e0
+ 0 ffffffff8101a514  ffffffff8101a772  mach_reboot_fixups <- native_machine_emergency_restart+0xe2/0x1e0
+ 0 ffffffff811d9c54  ffffffff8101a7a0  __const_udelay <- native_machine_emergency_restart+0x110/0x1e0
+ 0 ffffffff811d9c34  ffffffff811d9c80  __delay <- __const_udelay+0x30/0x40
+ 0 ffffffff811d9d14  ffffffff811d9c3f  delay_tsc <- __delay+0xf/0x20

+ 33 - 4
fs/pstore/ram.c

@@ -45,6 +45,10 @@ static ulong ramoops_console_size = MIN_MEM_SIZE;
 module_param_named(console_size, ramoops_console_size, ulong, 0400);
 module_param_named(console_size, ramoops_console_size, ulong, 0400);
 MODULE_PARM_DESC(console_size, "size of kernel console log");
 MODULE_PARM_DESC(console_size, "size of kernel console log");
 
 
+static ulong ramoops_ftrace_size = MIN_MEM_SIZE;
+module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400);
+MODULE_PARM_DESC(ftrace_size, "size of ftrace log");
+
 static ulong mem_address;
 static ulong mem_address;
 module_param(mem_address, ulong, 0400);
 module_param(mem_address, ulong, 0400);
 MODULE_PARM_DESC(mem_address,
 MODULE_PARM_DESC(mem_address,
@@ -70,16 +74,19 @@ MODULE_PARM_DESC(ramoops_ecc,
 struct ramoops_context {
 struct ramoops_context {
 	struct persistent_ram_zone **przs;
 	struct persistent_ram_zone **przs;
 	struct persistent_ram_zone *cprz;
 	struct persistent_ram_zone *cprz;
+	struct persistent_ram_zone *fprz;
 	phys_addr_t phys_addr;
 	phys_addr_t phys_addr;
 	unsigned long size;
 	unsigned long size;
 	size_t record_size;
 	size_t record_size;
 	size_t console_size;
 	size_t console_size;
+	size_t ftrace_size;
 	int dump_oops;
 	int dump_oops;
 	int ecc_size;
 	int ecc_size;
 	unsigned int max_dump_cnt;
 	unsigned int max_dump_cnt;
 	unsigned int dump_write_cnt;
 	unsigned int dump_write_cnt;
 	unsigned int dump_read_cnt;
 	unsigned int dump_read_cnt;
 	unsigned int console_read_cnt;
 	unsigned int console_read_cnt;
+	unsigned int ftrace_read_cnt;
 	struct pstore_info pstore;
 	struct pstore_info pstore;
 };
 };
 
 
@@ -137,6 +144,9 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
 	if (!prz)
 	if (!prz)
 		prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
 		prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
 					   1, id, type, PSTORE_TYPE_CONSOLE, 0);
 					   1, id, type, PSTORE_TYPE_CONSOLE, 0);
+	if (!prz)
+		prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt,
+					   1, id, type, PSTORE_TYPE_FTRACE, 0);
 	if (!prz)
 	if (!prz)
 		return 0;
 		return 0;
 
 
@@ -186,6 +196,11 @@ static int ramoops_pstore_write_buf(enum pstore_type_id type,
 			return -ENOMEM;
 			return -ENOMEM;
 		persistent_ram_write(cxt->cprz, buf, size);
 		persistent_ram_write(cxt->cprz, buf, size);
 		return 0;
 		return 0;
+	} else if (type == PSTORE_TYPE_FTRACE) {
+		if (!cxt->fprz)
+			return -ENOMEM;
+		persistent_ram_write(cxt->fprz, buf, size);
+		return 0;
 	}
 	}
 
 
 	if (type != PSTORE_TYPE_DMESG)
 	if (type != PSTORE_TYPE_DMESG)
@@ -235,6 +250,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id,
 	case PSTORE_TYPE_CONSOLE:
 	case PSTORE_TYPE_CONSOLE:
 		prz = cxt->cprz;
 		prz = cxt->cprz;
 		break;
 		break;
+	case PSTORE_TYPE_FTRACE:
+		prz = cxt->fprz;
+		break;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -348,7 +366,8 @@ static int __devinit ramoops_probe(struct platform_device *pdev)
 	if (cxt->max_dump_cnt)
 	if (cxt->max_dump_cnt)
 		goto fail_out;
 		goto fail_out;
 
 
-	if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size)) {
+	if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size &&
+			!pdata->ftrace_size)) {
 		pr_err("The memory size and the record/console size must be "
 		pr_err("The memory size and the record/console size must be "
 			"non-zero\n");
 			"non-zero\n");
 		goto fail_out;
 		goto fail_out;
@@ -357,18 +376,20 @@ static int __devinit ramoops_probe(struct platform_device *pdev)
 	pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
 	pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
 	pdata->record_size = rounddown_pow_of_two(pdata->record_size);
 	pdata->record_size = rounddown_pow_of_two(pdata->record_size);
 	pdata->console_size = rounddown_pow_of_two(pdata->console_size);
 	pdata->console_size = rounddown_pow_of_two(pdata->console_size);
+	pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
 
 
 	cxt->dump_read_cnt = 0;
 	cxt->dump_read_cnt = 0;
 	cxt->size = pdata->mem_size;
 	cxt->size = pdata->mem_size;
 	cxt->phys_addr = pdata->mem_address;
 	cxt->phys_addr = pdata->mem_address;
 	cxt->record_size = pdata->record_size;
 	cxt->record_size = pdata->record_size;
 	cxt->console_size = pdata->console_size;
 	cxt->console_size = pdata->console_size;
+	cxt->ftrace_size = pdata->ftrace_size;
 	cxt->dump_oops = pdata->dump_oops;
 	cxt->dump_oops = pdata->dump_oops;
 	cxt->ecc_size = pdata->ecc_size;
 	cxt->ecc_size = pdata->ecc_size;
 
 
 	paddr = cxt->phys_addr;
 	paddr = cxt->phys_addr;
 
 
-	dump_mem_sz = cxt->size - cxt->console_size;
+	dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size;
 	err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz);
 	err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz);
 	if (err)
 	if (err)
 		goto fail_out;
 		goto fail_out;
@@ -377,9 +398,14 @@ static int __devinit ramoops_probe(struct platform_device *pdev)
 	if (err)
 	if (err)
 		goto fail_init_cprz;
 		goto fail_init_cprz;
 
 
-	if (!cxt->przs && !cxt->cprz) {
+	err = ramoops_init_prz(dev, cxt, &cxt->fprz, &paddr, cxt->ftrace_size);
+	if (err)
+		goto fail_init_fprz;
+
+	if (!cxt->przs && !cxt->cprz && !cxt->fprz) {
 		pr_err("memory size too small, minimum is %lu\n",
 		pr_err("memory size too small, minimum is %lu\n",
-			cxt->console_size + cxt->record_size);
+			cxt->console_size + cxt->record_size +
+			cxt->ftrace_size);
 		goto fail_cnt;
 		goto fail_cnt;
 	}
 	}
 
 
@@ -426,6 +452,8 @@ fail_clear:
 	cxt->pstore.bufsize = 0;
 	cxt->pstore.bufsize = 0;
 	cxt->max_dump_cnt = 0;
 	cxt->max_dump_cnt = 0;
 fail_cnt:
 fail_cnt:
+	kfree(cxt->fprz);
+fail_init_fprz:
 	kfree(cxt->cprz);
 	kfree(cxt->cprz);
 fail_init_cprz:
 fail_init_cprz:
 	ramoops_free_przs(cxt);
 	ramoops_free_przs(cxt);
@@ -480,6 +508,7 @@ static void ramoops_register_dummy(void)
 	dummy_data->mem_address = mem_address;
 	dummy_data->mem_address = mem_address;
 	dummy_data->record_size = record_size;
 	dummy_data->record_size = record_size;
 	dummy_data->console_size = ramoops_console_size;
 	dummy_data->console_size = ramoops_console_size;
+	dummy_data->ftrace_size = ramoops_ftrace_size;
 	dummy_data->dump_oops = dump_oops;
 	dummy_data->dump_oops = dump_oops;
 	/*
 	/*
 	 * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC
 	 * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC

+ 1 - 0
include/linux/pstore_ram.h

@@ -72,6 +72,7 @@ struct ramoops_platform_data {
 	unsigned long	mem_address;
 	unsigned long	mem_address;
 	unsigned long	record_size;
 	unsigned long	record_size;
 	unsigned long	console_size;
 	unsigned long	console_size;
+	unsigned long	ftrace_size;
 	int		dump_oops;
 	int		dump_oops;
 	int		ecc_size;
 	int		ecc_size;
 };
 };