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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Mar 30, 2018 at 11:15:21AM +0800, Leo Yan wrote:
> After kernel panic happens, Coresight tracing data has much useful info
> which can be used for analysis.  For example, the trace info from ETB
> RAM can be used to check the CPU execution flows before the 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 the vmcore file.
> 
> This patch is to add a simple framework to support panic dump
> functionality; it registers panic notifier, and provide the helper
> functions coresight_kdump_source()/coresight_kdump_sink() so Coresight
> source and sink devices can be recorded into Coresight kdump node for
> kernel panic kdump.
> 
> When kernel panic happens, the notifier iterates dump array and invoke
> callback function to dump tracing data.  Later the tracing data can be
> used to reverse execution flow before the kernel panic.
> 
> Signed-off-by: Leo Yan <leo.yan@xxxxxxxxxx>
> ---
>  drivers/hwtracing/coresight/Kconfig                |   9 +
>  drivers/hwtracing/coresight/Makefile               |   1 +
>  .../hwtracing/coresight/coresight-panic-kdump.c    | 199 +++++++++++++++++++++
>  drivers/hwtracing/coresight/coresight-priv.h       |  12 ++
>  include/linux/coresight.h                          |   4 +
>  5 files changed, 225 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..3089abf 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 kernel panic happen Coresight device supplied callback function

s/Coresight/CoreSight

> +	  is to dump trace data to memory. From then on, kdump can be used 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..f4589e9
> --- /dev/null
> +++ b/drivers/hwtracing/coresight/coresight-panic-kdump.c
> @@ -0,0 +1,199 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2017~2018 Linaro Limited.

I don't remember if I commented on this before but the above line (not the SPDX)
should be enclosed with C style comments (/* */) rather than C++ (//).

I would also add a new line between the copyright statement and the header file
listing.

> +#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"
> +
> +/**
> + * struct coresight_kdump_node - Node information for dump
> + * @source_csdev:	Handler for source coresight device
> + * @sink_csdev:		Handler for sink coresight device
> + */
> +struct coresight_kdump_node {
> +	struct coresight_device *source_csdev;
> +	struct coresight_device *sink_csdev;
> +};
> +
> +static DEFINE_SPINLOCK(coresight_kdump_lock);
> +static struct coresight_kdump_node *coresight_kdump_nodes;
> +static struct notifier_block coresight_kdump_nb;
> +
> +/**
> + * coresight_kdump_source - Set source dump info for specific CPU
> + * @cpu:	CPU ID
> + * @csdev:	Source device structure handler
> + * @data:	Pointer for source device metadata buffer
> + * @data_sz:	Size of source device metadata buffer
> + *
> + * This function is a helper function which is used to set/clear source device
> + * handler and metadata when the tracer is enabled; and it can be used to clear
> + * source device related info when the tracer is disabled.
> + *
> + * Returns:	0 on success, negative errno otherwise.
> + */
> +int coresight_kdump_source(int cpu, struct coresight_device *csdev,
> +			   char *data, unsigned int data_sz)
> +{
> +	struct coresight_kdump_node *node;
> +	unsigned long flags;
> +
> +	if (!coresight_kdump_nodes)
> +		return -EPROBE_DEFER;

Before grabbing the lock you also need to make sure
@cpu is < num_possible_cpus().

> +
> +	spin_lock_irqsave(&coresight_kdump_lock, flags);
> +
> +	node = &coresight_kdump_nodes[cpu];
> +	node->source_csdev = csdev;
> +
> +	csdev->kdump_buf = data;
> +	csdev->kdump_buf_sz = data_sz;
> +
> +	spin_unlock_irqrestore(&coresight_kdump_lock, flags);
> +	return 0;
> +}
> +
> +/**
> + * coresight_kdump_sink - Set sink device handler for specific CPU
> + * @cpu:	CPU ID
> + * @csdev:	Sink device structure handler
> + *
> + * This function is a helper function which is used to set sink device handler
> + * when the Coresight path has been enabled for specific CPU; and it can be used
> + * to clear sink device handler when the path is disabled.
> + *
> + * Returns:	0 on success, negative errno otherwise.
> + */
> +int coresight_kdump_sink(int cpu, struct coresight_device *csdev)
> +{
> +	struct coresight_kdump_node *node;
> +	unsigned long flags;
> +
> +	if (!coresight_kdump_nodes)
> +		return -EPROBE_DEFER;

Same comment as above.

> +
> +	spin_lock_irqsave(&coresight_kdump_lock, flags);
> +
> +	node = &coresight_kdump_nodes[cpu];
> +	node->sink_csdev = csdev;

	csdev->kdump_buf = NULL;
        csdev->kdump_buf_sz = 0;

> +
> +	spin_unlock_irqrestore(&coresight_kdump_lock, flags);
> +	return 0;
> +}
> +
> +/**
> + * coresight_kdump_sink_cb - Invoke sink callback for specific CPU
> + * @cpu:	CPU ID
> + *
> + * This function is to invoke sink device corresponding callback.  It needs
> + * to check two cases: one case is the CPU has not been enabled for Coresight
> + * path so there totally has no trace data for the CPU, another case is the
> + * CPU shares the same sink device with other CPUs but the tracing data has
> + * been dumped by previous CPUs; it skips dump for these two cases.
> + */
> +static void coresight_kdump_sink_cb(int cpu)
> +{
> +	struct coresight_kdump_node *node;
> +	struct coresight_device *csdev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&coresight_kdump_lock, flags);
> +
> +	node = &coresight_kdump_nodes[cpu];
> +	csdev = node->sink_csdev;
> +
> +	/* Path has not been enabled */
> +	if (!csdev)
> +		goto skip_dump;
> +
> +	/* Have been dumped by previous CPU */
> +	if (csdev->kdump_buf)

I would use csdev->kdump_buf_sz instead of csdev->kdump_buf.  The reason is that
the sink may not always use an internal SRAM buffer.  For instance the ETR uses
system RAM, either as a contiguous buffer or a scatter-gather list.  When the
panic callback for an ETR is implemented the code in the core (i.e this file)
need not change as kdump_buf_sz is independent of the way data is conveyed.

> +		goto skip_dump;
> +
> +	/* Invoke panic callback */
> +	csdev = coresight_kdump_nodes[cpu].sink_csdev;
> +	if (csdev && sink_ops(csdev)->panic_cb)
> +		sink_ops(csdev)->panic_cb(csdev);
> +
> +skip_dump:
> +	spin_unlock_irqrestore(&coresight_kdump_lock, flags);
> +}
> +
> +/**
> + * coresight_kdump_notify - Invoke panic dump callbacks
> + * @nb:		Pointer to notifier block
> + * @event:	Notification reason
> + * @_unused:	Pointer to notification data object, unused
> + *
> + * This function is called when panic happens to invoke dump callbacks, it takes
> + * panic CPU tracing data with high priority to firstly invoke panic CPU sink
> + * callback function, then the notifier iterates callback functions one by one
> + * for other CPUs.  If one sink device is shared among CPUs, the sink panic
> + * callback is invoked for the first traversed CPU node and other sequential
> + * CPUs are skipped.
> + *
> + * Returns:	0 on success.
> + */
> +static int coresight_kdump_notify(struct notifier_block *nb,
> +				  unsigned long event, void *_unused)
> +{
> +	int cpu, first;
> +
> +	/* Give panic CPU trace data with high priority */

I would replace the above comment with "Start with the panic'ed CPU".


> +	first = atomic_read(&panic_cpu);
> +	coresight_kdump_sink_cb(first);
> +
> +	/* Dump rest CPUs trace data */
> +	for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
> +		if (cpu == first)
> +			continue;
> +
> +		coresight_kdump_sink_cb(cpu);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * coresight_kdump_init - Coresight kdump module initialization
> + *
> + * This function allcoates dump array and register panic norifier.
> + *
> + * Returns:	0 on success, negative errno otherwise.
> + */
> +static int __init coresight_kdump_init(void)
> +{
> +	int ret;
> +
> +	coresight_kdump_nodes = kmalloc_array(num_possible_cpus(),
> +					      sizeof(*coresight_kdump_nodes),
> +					      GFP_KERNEL);
> +	if (!coresight_kdump_nodes) {
> +		pr_err("%s: kmalloc failed\n", __func__);
> +		return -ENOMEM;
> +	}
> +
> +	memset(coresight_kdump_nodes, 0,
> +	       num_possible_cpus() * sizeof(*coresight_kdump_nodes));

If you use kcalloc() above you don't need to explicitly zero out the memory.

> +
> +	coresight_kdump_nb.notifier_call = coresight_kdump_notify;
> +	ret = atomic_notifier_chain_register(&panic_notifier_list,
> +					     &coresight_kdump_nb);
> +	if (ret) {
> +		pr_err("%s: unable to register notifier: %d\n",
> +		       __func__, ret);
> +		kfree(coresight_kdump_nodes);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +postcore_initcall(coresight_kdump_init);
> diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
> index f1d0e21d..76d27d6 100644
> --- a/drivers/hwtracing/coresight/coresight-priv.h
> +++ b/drivers/hwtracing/coresight/coresight-priv.h
> @@ -151,4 +151,16 @@ 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_source(int cpu, struct coresight_device *csdev,
> +				  char *data, unsigned int data_sz);
> +extern int coresight_kdump_sink(int cpu, struct coresight_device *csdev);
> +#else
> +static inline int coresight_kdump_source(int cpu,
> +		struct coresight_device *csdev,
> +		char *data, unsigned int data_sz) { return 0; }
> +static inline void coresight_kdump_sink(int cpu,
> +		struct coresight_device *csdev) { return 0; }

To me the above is harder to read - I suggest:

static inline int
coresight_kdump_source(int cpu,
                       struct coresight_device *csdev,
                       char *data, unsigned int data_sz) { return 0; }
static inline void
coresight_kdump_sink(int cpu,
                     struct coresight_device *csdev) { return 0; }

> +#endif
> +
>  #endif
> diff --git a/include/linux/coresight.h b/include/linux/coresight.h
> index d950dad..89aad8d 100644
> --- a/include/linux/coresight.h
> +++ b/include/linux/coresight.h
> @@ -171,6 +171,8 @@ 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 */
> +	char *kdump_buf;
> +	unsigned int kdump_buf_sz;

Please add structure documentation, the same way all the other fields in this
structure is.

>  };
>  
>  #define to_coresight_device(d) container_of(d, struct coresight_device, dev)
> @@ -189,6 +191,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 +208,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);
>  };
>  
>  /**
> -- 
> 2.7.4
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux