summaryrefslogtreecommitdiff
path: root/drivers/hwtracing
diff options
context:
space:
mode:
authorSatyajit Desai <sadesai@codeaurora.org>2016-06-27 12:39:34 -0700
committerSatyajit Desai <sadesai@codeaurora.org>2016-08-19 14:56:53 -0700
commitc34bf4be22d64499449d1cfe550b05365d048403 (patch)
tree9a1558daae6ed11ba043063b2ab588c227e191c6 /drivers/hwtracing
parent0c8cf716fa02d0f6b6ef2ef1446f9f4819fc83dd (diff)
coresight: abort coresight tracing on kernel crash
Add trace events to control aborting CoreSight trace dynamically based on module parameter. Coresight driver will dump any trace present in the current sink in case we hit a kernel panic, user fault or an undefined instruction. Change-Id: Iee1ccf5cbd7b767753a3115c0570e63fbe2aa8f3 Signed-off-by: Satyajit Desai <sadesai@codeaurora.org>
Diffstat (limited to 'drivers/hwtracing')
-rw-r--r--drivers/hwtracing/coresight/Kconfig8
-rw-r--r--drivers/hwtracing/coresight/Makefile1
-rw-r--r--drivers/hwtracing/coresight/coresight-event.c169
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c35
-rw-r--r--drivers/hwtracing/coresight/coresight-tpiu.c12
-rw-r--r--drivers/hwtracing/coresight/coresight.c19
6 files changed, 243 insertions, 1 deletions
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 3228282dc49c..8c92a564299d 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -13,6 +13,14 @@ menuconfig CORESIGHT
if CORESIGHT
+config CORESIGHT_EVENT
+ tristate "CoreSight Event driver"
+ help
+ This driver provides support for registering with various events
+ and performing CoreSight actions like aborting trace on their
+ occurrence. These events can be controlled by using module
+ parameters.
+
config CORESIGHT_CSR
bool "CoreSight Slave Register driver"
help
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index ee3a77fede53..09433897b6a2 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CORESIGHT) += coresight.o
obj-$(CONFIG_OF) += of_coresight.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
+obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
diff --git a/drivers/hwtracing/coresight/coresight-event.c b/drivers/hwtracing/coresight/coresight-event.c
new file mode 100644
index 000000000000..0bced010d4c5
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-event.c
@@ -0,0 +1,169 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/coresight.h>
+
+#include <trace/events/exception.h>
+
+static int event_abort_enable;
+static int event_abort_set(const char *val, struct kernel_param *kp);
+module_param_call(event_abort_enable, event_abort_set, param_get_int,
+ &event_abort_enable, 0644);
+
+static int event_abort_early_panic = 1;
+static int event_abort_on_panic_set(const char *val, struct kernel_param *kp);
+module_param_call(event_abort_early_panic, event_abort_on_panic_set,
+ param_get_int, &event_abort_early_panic, 0644);
+
+static void event_abort_user_fault(void *ignore,
+ struct task_struct *task,
+ unsigned long addr,
+ unsigned int fsr)
+{
+ coresight_abort();
+ pr_debug("coresight_event: task_name: %s, addr: %lu, fsr:%u",
+ (char *)task->comm, addr, fsr);
+}
+
+static void event_abort_undef_instr(void *ignore,
+ struct pt_regs *regs,
+ void *pc)
+{
+ if (user_mode(regs)) {
+ coresight_abort();
+ pr_debug("coresight_event: pc: %p", pc);
+ }
+}
+
+static void event_abort_unhandled_abort(void *ignore,
+ struct pt_regs *regs,
+ unsigned long addr,
+ unsigned int fsr)
+{
+ if (user_mode(regs)) {
+ coresight_abort();
+ pr_debug("coresight_event: addr: %lu, fsr:%u", addr, fsr);
+ }
+}
+
+static void event_abort_kernel_panic(void *ignore, long state)
+{
+ coresight_abort();
+}
+
+static int event_abort_register(void)
+{
+ int ret;
+
+ ret = register_trace_user_fault(event_abort_user_fault, NULL);
+ if (ret)
+ goto err_usr_fault;
+ ret = register_trace_undef_instr(event_abort_undef_instr, NULL);
+ if (ret)
+ goto err_undef_instr;
+ ret = register_trace_unhandled_abort(event_abort_unhandled_abort, NULL);
+ if (ret)
+ goto err_unhandled_abort;
+
+ return 0;
+
+err_unhandled_abort:
+ unregister_trace_undef_instr(event_abort_undef_instr, NULL);
+err_undef_instr:
+ unregister_trace_user_fault(event_abort_user_fault, NULL);
+err_usr_fault:
+ return ret;
+}
+
+static void event_abort_unregister(void)
+{
+ unregister_trace_user_fault(event_abort_user_fault, NULL);
+ unregister_trace_undef_instr(event_abort_undef_instr, NULL);
+ unregister_trace_unhandled_abort(event_abort_unhandled_abort, NULL);
+}
+
+static int event_abort_set(const char *val, struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_int(val, kp);
+ if (ret) {
+ pr_err("coresight_event: error setting value %d\n", ret);
+ return ret;
+ }
+
+ if (event_abort_enable)
+ ret = event_abort_register();
+ else
+ event_abort_unregister();
+
+ return ret;
+}
+
+static int event_abort_on_panic_set(const char *val, struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_int(val, kp);
+ if (ret) {
+ pr_err("coresight_event: error setting val on panic %d\n", ret);
+ return ret;
+ }
+
+ if (event_abort_early_panic) {
+ unregister_trace_kernel_panic_late(event_abort_kernel_panic,
+ NULL);
+ ret = register_trace_kernel_panic(event_abort_kernel_panic,
+ NULL);
+ if (ret)
+ goto err;
+ } else {
+ unregister_trace_kernel_panic(event_abort_kernel_panic, NULL);
+ ret = register_trace_kernel_panic_late(event_abort_kernel_panic,
+ NULL);
+ if (ret)
+ goto err;
+ }
+ return 0;
+err:
+ pr_err("coresight_event: error registering panic event %d\n", ret);
+ return ret;
+}
+
+static int __init event_init(void)
+{
+ int ret;
+
+ ret = register_trace_kernel_panic(event_abort_kernel_panic, NULL);
+ if (ret) {
+ /* We do not want to fail module init. This module can still
+ * be used to register other abort events.
+ */
+ pr_err("coresight_event: error registering on panic %d\n", ret);
+ }
+ return 0;
+}
+module_init(event_init);
+
+static void __exit event_exit(void)
+{
+ unregister_trace_kernel_panic(event_abort_kernel_panic, NULL);
+}
+module_exit(event_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Coresight Event driver to abort tracing");
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 10e50df1e6d5..d48d8485f979 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -1064,9 +1064,44 @@ static void tmc_disable_link(struct coresight_device *csdev, int inport,
tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO);
}
+static void tmc_abort(struct coresight_device *csdev)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ unsigned long flags;
+ enum tmc_mode mode;
+
+ spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (drvdata->reading)
+ goto out0;
+
+ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
+ tmc_etb_disable_hw(drvdata);
+ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+ if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+ tmc_etr_disable_hw(drvdata);
+ else if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB)
+ __tmc_etr_disable_to_bam(drvdata);
+ } else {
+ mode = readl_relaxed(drvdata->base + TMC_MODE);
+ if (mode == TMC_MODE_CIRCULAR_BUFFER)
+ tmc_etb_disable_hw(drvdata);
+ else
+ goto out1;
+ }
+out0:
+ drvdata->enable = false;
+ spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+ dev_info(drvdata->dev, "TMC aborted\n");
+ return;
+out1:
+ spin_unlock_irqrestore(&drvdata->spinlock, flags);
+}
+
static const struct coresight_ops_sink tmc_sink_ops = {
.enable = tmc_enable_sink,
.disable = tmc_disable_sink,
+ .abort = tmc_abort,
};
static const struct coresight_ops_link tmc_link_ops = {
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index 7214efd10db5..7baa1e750a23 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2012, 2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -103,9 +103,19 @@ static void tpiu_disable(struct coresight_device *csdev)
dev_info(drvdata->dev, "TPIU disabled\n");
}
+static void tpiu_abort(struct coresight_device *csdev)
+{
+ struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ tpiu_disable_hw(drvdata);
+
+ dev_info(drvdata->dev, "TPIU aborted\n");
+}
+
static const struct coresight_ops_sink tpiu_sink_ops = {
.enable = tpiu_enable,
.disable = tpiu_disable,
+ .abort = tpiu_abort,
};
static const struct coresight_ops tpiu_cs_ops = {
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index a4d2ac601556..c34599c0594d 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -383,6 +383,25 @@ out:
}
EXPORT_SYMBOL_GPL(coresight_disable);
+void coresight_abort(void)
+{
+ if (!mutex_trylock(&coresight_mutex)) {
+ pr_err_ratelimited("coresight: abort could not be processed\n");
+ return;
+ }
+ if (!curr_sink)
+ goto out;
+
+ if (curr_sink->enable && sink_ops(curr_sink)->abort) {
+ sink_ops(curr_sink)->abort(curr_sink);
+ curr_sink->enable = false;
+ }
+
+out:
+ mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_abort);
+
static int coresight_disable_all_source(struct device *dev, void *data)
{
struct coresight_device *csdev;