diff options
-rw-r--r-- | drivers/hwtracing/coresight/Kconfig | 9 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-panic-kdump.c | 154 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-priv.h | 13 | ||||
-rw-r--r-- | include/linux/coresight.h | 7 |
5 files changed, 184 insertions, 0 deletions
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ef9cb3c164e1f..4812529ee2ab2 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -103,4 +103,13 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage. +config CORESIGHT_PANIC_KDUMP + bool "CoreSight Panic Kdump driver" + depends on ARM || ARM64 + help + This driver provides panic kdump functionality for CoreSight + devices. When a kernel panic happen a device supplied callback function + is used to save trace data to memory. From there we rely on kdump to extract + the trace data from kernel dump file. + endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 61db9dd0d571f..946fe19736eaf 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o +obj-$(CONFIG_CORESIGHT_PANIC_KDUMP) += coresight-panic-kdump.o diff --git a/drivers/hwtracing/coresight/coresight-panic-kdump.c b/drivers/hwtracing/coresight/coresight-panic-kdump.c new file mode 100644 index 0000000000000..c21d20b9ebc9c --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-panic-kdump.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Linaro Limited. +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpumask.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/perf_event.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "coresight-priv.h" + +typedef void (*coresight_cb_t)(void *data); + +/** + * struct coresight_kdump_node - Node information for dump + * @cpu: The cpu this node is affined to. + * @csdev: Handler for coresight device. + * @buf: Pointer for dump buffer. + * @buf_size: Length of dump buffer. + * @list: Hook to the list. + */ +struct coresight_kdump_node { + int cpu; + struct coresight_device *csdev; + char *buf; + unsigned int buf_size; + struct list_head list; +}; + +static DEFINE_SPINLOCK(coresight_kdump_lock); +static LIST_HEAD(coresight_kdump_list); +static struct notifier_block coresight_kdump_nb; + +int coresight_kdump_update(struct coresight_device *csdev, char *buf, + unsigned int buf_size) +{ + struct coresight_kdump_node *node = csdev->dump_node; + + if (!node) { + dev_err(&csdev->dev, "Failed to update dump node.\n"); + return -EINVAL; + } + + node->buf = buf; + node->buf_size = buf_size; + return 0; +} + +int coresight_kdump_add(struct coresight_device *csdev, int cpu) +{ + struct coresight_kdump_node *node; + unsigned long flags; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + csdev->dump_node = (void *)node; + node->cpu = cpu; + node->csdev = csdev; + + spin_lock_irqsave(&coresight_kdump_lock, flags); + list_add_tail(&node->list, &coresight_kdump_list); + spin_unlock_irqrestore(&coresight_kdump_lock, flags); + return 0; +} + +void coresight_kdump_del(struct coresight_device *csdev) +{ + struct coresight_kdump_node *node, *next; + unsigned long flags; + + spin_lock_irqsave(&coresight_kdump_lock, flags); + list_for_each_entry_safe(node, next, &coresight_kdump_list, list) { + if (node->csdev == csdev) { + list_del(&node->list); + kfree(node); + break; + } + } + spin_unlock_irqrestore(&coresight_kdump_lock, flags); +} + +static coresight_cb_t +coresight_kdump_get_cb(struct coresight_device *csdev) +{ + coresight_cb_t cb = NULL; + + switch (csdev->type) { + case CORESIGHT_DEV_TYPE_SINK: + case CORESIGHT_DEV_TYPE_LINKSINK: + cb = sink_ops(csdev)->panic_cb; + break; + case CORESIGHT_DEV_TYPE_SOURCE: + cb = source_ops(csdev)->panic_cb; + break; + case CORESIGHT_DEV_TYPE_LINK: + cb = link_ops(csdev)->panic_cb; + break; + default: + dev_info(&csdev->dev, "Unsupport panic dump\n"); + break; + } + + return cb; +} + +/** + * coresight_kdump_notify - Invoke panic dump callbacks, this is + * the main function to fulfill the panic dump. It distinguishs + * to two types: one is pre panic dump which the callback function + * handler is NULL and coresight drivers can use function + * coresight_kdump_update() to directly update dump buffer base + * address and buffer size, for this case this function does nothing + * and directly bail out; another case is for post panic dump so + * invoke callback on alive CPU. + * + * Returns: 0 on success. + */ +static int coresight_kdump_notify(struct notifier_block *nb, + unsigned long mode, void *_unused) +{ + struct coresight_kdump_node *node; + struct coresight_device *csdev; + coresight_cb_t cb; + unsigned long flags; + + spin_lock_irqsave(&coresight_kdump_lock, flags); + + list_for_each_entry(node, &coresight_kdump_list, list) { + csdev = node->csdev; + cb = coresight_kdump_get_cb(csdev); + if (cb) + cb(csdev); + } + + spin_unlock_irqrestore(&coresight_kdump_lock, flags); + return 0; +} + +static int __init coresight_kdump_init(void) +{ + int ret; + + coresight_kdump_nb.notifier_call = coresight_kdump_notify; + ret = atomic_notifier_chain_register(&panic_notifier_list, + &coresight_kdump_nb); + return ret; +} +late_initcall(coresight_kdump_init); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index f1d0e21d8cabb..937750e26d1e1 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -151,4 +151,17 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif +#ifdef CONFIG_CORESIGHT_PANIC_KDUMP +extern int coresight_kdump_add(struct coresight_device *csdev, int cpu); +extern void coresight_kdump_del(struct coresight_device *csdev); +extern int coresight_kdump_update(struct coresight_device *csdev, + char *buf, unsigned int buf_size); +#else +static inline int +coresight_kdump_add(struct coresight_device *csdev, int cpu) { return 0; } +static inline void coresight_kdump_del(struct coresight_device *csdev) {} +static inline int coresight_kdump_update(struct coresight_device *csdev, + char *buf, unsigned int buf_size) { return 0; } +#endif + #endif diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d950dad5056aa..43e40fa3a7b3c 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -171,6 +171,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */ + void *dump_node; }; #define to_coresight_device(d) container_of(d, struct coresight_device, dev) @@ -189,6 +190,7 @@ struct coresight_device { * @set_buffer: initialises buffer mechanic before a trace session. * @reset_buffer: finalises buffer mechanic after a trace session. * @update_buffer: update buffer pointers after a trace session. + * @panic_cb: hook function for panic notifier. */ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode); @@ -205,6 +207,7 @@ struct coresight_ops_sink { void (*update_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config); + void (*panic_cb)(void *data); }; /** @@ -212,10 +215,12 @@ struct coresight_ops_sink { * Operations available for links. * @enable: enables flow between iport and oport. * @disable: disables flow between iport and oport. + * @panic_cb: hook function for panic notifier. */ struct coresight_ops_link { int (*enable)(struct coresight_device *csdev, int iport, int oport); void (*disable)(struct coresight_device *csdev, int iport, int oport); + void (*panic_cb)(void *data); }; /** @@ -227,6 +232,7 @@ struct coresight_ops_link { * to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. + * @panic_cb: hook function for panic notifier. */ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); @@ -235,6 +241,7 @@ struct coresight_ops_source { struct perf_event *event, u32 mode); void (*disable)(struct coresight_device *csdev, struct perf_event *event); + void (*panic_cb)(void *data); }; struct coresight_ops { |