summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/kernel/traps.c4
-rw-r--r--arch/arm64/mm/fault.c4
-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
-rw-r--r--include/linux/coresight.h4
-rw-r--r--include/trace/events/exception.h124
-rw-r--r--kernel/panic.c8
11 files changed, 387 insertions, 1 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index e8b1f7910490..48b75ece4c17 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -44,6 +44,8 @@
#include <asm/esr.h>
#include <asm/edac.h>
+#include <trace/events/exception.h>
+
static const char *handler[]= {
"Synchronous Abort",
"IRQ",
@@ -421,6 +423,8 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
if (call_undef_hook(regs) == 0)
return;
+ trace_undef_instr(regs, (void *)pc);
+
if (unhandled_signal(current, SIGILL) && show_unhandled_signals_ratelimited()) {
pr_info("%s[%d]: undefined instruction: pc=%p\n",
current->comm, task_pid_nr(current), pc);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 7bb08670fc10..69079e5bfc84 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -40,6 +40,8 @@
#include <asm/tlbflush.h>
#include <asm/edac.h>
+#include <trace/events/exception.h>
+
static const char *fault_name(unsigned int esr);
/*
@@ -118,6 +120,8 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
{
struct siginfo si;
+ trace_user_fault(tsk, addr, esr);
+
if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) {
pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n",
tsk->comm, task_pid_nr(tsk), fault_name(esr), sig,
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;
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 903a8e852f5d..66bf56640fe1 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -194,10 +194,12 @@ struct coresight_device {
* Operations available for sinks
* @enable: enables the sink.
* @disable: disables the sink.
+ * @abort: captures sink trace on abort
*/
struct coresight_ops_sink {
int (*enable)(struct coresight_device *csdev);
void (*disable)(struct coresight_device *csdev);
+ void (*abort)(struct coresight_device *csdev);
};
/**
@@ -239,6 +241,7 @@ extern int coresight_enable(struct coresight_device *csdev);
extern void coresight_disable(struct coresight_device *csdev);
extern int coresight_timeout(void __iomem *addr, u32 offset,
int position, int value);
+extern void coresight_abort(void);
#else
static inline struct coresight_device *
coresight_register(struct coresight_desc *desc) { return NULL; }
@@ -248,6 +251,7 @@ coresight_enable(struct coresight_device *csdev) { return -ENOSYS; }
static inline void coresight_disable(struct coresight_device *csdev) {}
static inline int coresight_timeout(void __iomem *addr, u32 offset,
int position, int value) { return 1; }
+static inline void coresight_abort(void) {}
#endif
#if defined(CONFIG_OF) && defined(CONFIG_CORESIGHT)
diff --git a/include/trace/events/exception.h b/include/trace/events/exception.h
new file mode 100644
index 000000000000..6b525da1432e
--- /dev/null
+++ b/include/trace/events/exception.h
@@ -0,0 +1,124 @@
+/* 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM exception
+
+#if !defined(_TRACE_EXCEPTION_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_EXCEPTION_H
+
+#include <linux/tracepoint.h>
+
+struct task_struct;
+
+TRACE_EVENT(user_fault,
+
+ TP_PROTO(struct task_struct *tsk, unsigned long addr, unsigned int fsr),
+
+ TP_ARGS(tsk, addr, fsr),
+
+ TP_STRUCT__entry(
+ __string(task_name, tsk->comm)
+ __field(unsigned long, addr)
+ __field(unsigned int, fsr)
+ ),
+
+ TP_fast_assign(
+ __assign_str(task_name, tsk->comm)
+ __entry->addr = addr;
+ __entry->fsr = fsr;
+ ),
+
+ TP_printk("task_name:%s addr:%lu, fsr:%u", __get_str(task_name),
+ __entry->addr, __entry->fsr)
+);
+
+
+struct pt_regs;
+
+TRACE_EVENT(undef_instr,
+
+ TP_PROTO(struct pt_regs *regs, void *prog_cnt),
+
+ TP_ARGS(regs, prog_cnt),
+
+ TP_STRUCT__entry(
+ __field(void *, prog_cnt)
+ __field(struct pt_regs *, regs)
+ ),
+
+ TP_fast_assign(
+ __entry->regs = regs;
+ __entry->prog_cnt = prog_cnt;
+ ),
+
+ TP_printk("pc:%p", __entry->prog_cnt)
+);
+
+TRACE_EVENT(unhandled_abort,
+
+ TP_PROTO(struct pt_regs *regs, unsigned long addr, unsigned int fsr),
+
+ TP_ARGS(regs, addr, fsr),
+
+ TP_STRUCT__entry(
+ __field(struct pt_regs *, regs)
+ __field(unsigned long, addr)
+ __field(unsigned int, fsr)
+ ),
+
+ TP_fast_assign(
+ __entry->regs = regs;
+ __entry->addr = addr;
+ __entry->fsr = fsr;
+ ),
+
+ TP_printk("addr:%lu, fsr:%u", __entry->addr, __entry->fsr)
+);
+
+TRACE_EVENT(kernel_panic,
+
+ TP_PROTO(long dummy),
+
+ TP_ARGS(dummy),
+
+ TP_STRUCT__entry(
+ __field(long, dummy)
+ ),
+
+ TP_fast_assign(
+ __entry->dummy = dummy;
+ ),
+
+ TP_printk("dummy:%ld", __entry->dummy)
+);
+
+TRACE_EVENT(kernel_panic_late,
+
+ TP_PROTO(long dummy),
+
+ TP_ARGS(dummy),
+
+ TP_STRUCT__entry(
+ __field(long, dummy)
+ ),
+
+ TP_fast_assign(
+ __entry->dummy = dummy;
+ ),
+
+ TP_printk("dummy:%ld", __entry->dummy)
+);
+
+#endif
+
+#include <trace/define_trace.h>
diff --git a/kernel/panic.c b/kernel/panic.c
index 223564d3e1f8..b4a0edc489c5 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -25,6 +25,9 @@
#include <linux/nmi.h>
#include <linux/console.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/exception.h>
+
#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18
@@ -80,6 +83,8 @@ void panic(const char *fmt, ...)
long i, i_next = 0;
int state = 0;
+ trace_kernel_panic(0);
+
/*
* Disable local interrupts. This will prevent panic_smp_self_stop
* from deadlocking the first cpu that invokes the panic, since
@@ -181,6 +186,9 @@ void panic(const char *fmt, ...)
mdelay(PANIC_TIMER_STEP);
}
}
+
+ trace_kernel_panic_late(0);
+
if (panic_timeout != 0) {
/*
* This will not be a clean reboot, with everything