Merge "coresight: abort coresight tracing on kernel crash"
This commit is contained in:
commit
30f32fdd4e
11 changed files with 387 additions and 1 deletions
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
169
drivers/hwtracing/coresight/coresight-event.c
Normal file
169
drivers/hwtracing/coresight/coresight-event.c
Normal file
|
@ -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");
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
124
include/trace/events/exception.h
Normal file
124
include/trace/events/exception.h
Normal file
|
@ -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>
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue