[PATCH v3 3/6] coresight: Support panic kdump functionality

Mathieu Poirier mathieu.poirier at linaro.org
Wed Jan 10 07:43:18 PST 2018


On 9 January 2018 at 22:19, Leo Yan <leo.yan at linaro.org> wrote:
> On Tue, Jan 09, 2018 at 11:41:26AM -0700, Mathieu Poirier wrote:
>> On Thu, Dec 21, 2017 at 04:20:12PM +0800, Leo Yan wrote:
>> > After kernel panic happens, coresight has many useful info can be used
>> > for analysis.  For example, the trace info from ETB RAM can be used to
>> > check the CPU execution flows before crash.  So we can save the tracing
>> > data from sink devices, and rely on kdump to save DDR content and uses
>> > "crash" tool to extract coresight dumping from vmcore file.
>> >
>> > This patch is to add a simple framework to support panic dump
>> > functionality; it registers panic notifier, and provide the general APIs
>> > {coresight_kdump_add|coresight_kdump_del} as helper functions so any
>> > coresight device can add itself into dump list or delete as needed.
>> >
>> > This driver provides helper function coresight_kdump_update() to update
>> > the dump buffer base address and buffer size.  This function can be used
>> > by coresight driver, e.g. it can be used to save ETM meta data info at
>> > runtime and these info can be prepared pre panic happening.
>> >
>> > When kernel panic happens, the notifier iterates dump list and calls
>> > callback function to dump device specific info.  The panic dump is
>> > mainly used to dump trace data so we can get to know the execution flow
>> > before the panic happens.
>> >
>> > Signed-off-by: Leo Yan <leo.yan at linaro.org>
>> > ---
>> >  drivers/hwtracing/coresight/Kconfig                |   9 ++
>> >  drivers/hwtracing/coresight/Makefile               |   1 +
>> >  .../hwtracing/coresight/coresight-panic-kdump.c    | 154 +++++++++++++++++++++
>> >  drivers/hwtracing/coresight/coresight-priv.h       |  13 ++
>> >  include/linux/coresight.h                          |   7 +
>> >  5 files changed, 184 insertions(+)
>> >  create mode 100644 drivers/hwtracing/coresight/coresight-panic-kdump.c
>> >
>> > diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
>> > index ef9cb3c..4812529 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
>>
>> At this time only ETMv4 supports the feature, so it is only ARM64.
>
> Thanks for reviewing, Mathieu.
>
> Will change to only for 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 61db9dd..946fe19 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 0000000..c21d20b
>> > --- /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;
>>
>> I don't see why we need a callback for link devices - didn't I raised
>> that question before?
>
> Yes, sorry I have not deleted for link devices completely. Will remove
> it.
>
>> And I've been thinking further about this.  The way we call the panic callbacks
>> won't work.  When a panic is triggered there might be trace data in the CS network
>> that hasn't made it to the sink yet and calling the panic callbacks for sinks
>> will lead to a loss of data.
>>
>> That is why, when accessing from both sysFS and perf, the current implementation
>> takes great care to stop the tracers first and then deal with the sink.  To fix
>> this I suggest to call the panic callbacks only for sources.  What happens there
>> will depend on what interface is used (sysFS or perf) - look at what is
>> currently done to get a better understanding.
>
> Will look into this.
>
> If I understand correctly, we need firstly stop tracers and save trace
> data from sink, right? If so we need use single callback function to
> disable path and dump data for sink, will study current case and check
> what's the clean method for kdump.

You are correct - only the callback for sources should be used.  In
that callback processing is different whether trace collection was
started from sysFS or perf.  The code already exists, it's just a
matter of doing the right thing.

>
>> > +   default:
>> > +           dev_info(&csdev->dev, "Unsupport panic dump\n");
>>
>> I would not bother with the dev_info()...
>
> Will remove it.
>
>> > +           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.
>>
>> Now that pre and post processing are gone the description above doesn't match
>> what the function is doing.
>
> Yeah, will remove 'pre' and 'post' to avoid confusion.
>
>> > + *
>> > + * 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 f1d0e21d..937750e 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; }
>>
>> static inline int
>> coresight_kdump_update(struct coresight_device *csdev, char *buf,
>>                        unsigned int buf_size) { return 0; }
>
> Will fix.
>
>> > +#endif
>> > +
>> >  #endif
>> > diff --git a/include/linux/coresight.h b/include/linux/coresight.h
>> > index d950dad..43e40fa 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;
>>
>> Please add a description for this entry.
>
> Will do.
>
> Thanks,
> Leo Yan
>
>> >  };
>> >
>> >  #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 {
>> > --
>> > 2.7.4
>> >



More information about the linux-arm-kernel mailing list