Merge "coresight: abort coresight tracing on kernel crash"

This commit is contained in:
Linux Build Service Account 2016-08-26 14:48:21 -07:00 committed by Gerrit - the friendly Code Review server
commit 30f32fdd4e
11 changed files with 387 additions and 1 deletions

View file

@ -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);

View file

@ -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,

View file

@ -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

View file

@ -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

View 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");

View file

@ -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 = {

View file

@ -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 = {

View file

@ -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;

View file

@ -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)

View 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>

View file

@ -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