Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52a0d3dc authored by Neeraj Upadhyay's avatar Neeraj Upadhyay
Browse files

soc: qcom: Enable ftrace support in minidump



Register and dump ftrace buffer on kernel panic, to minidump
table. The dumps are collected in parsed format.

Change-Id: I10605467d2b566425cc2cb4fd67dbc59fcd592e7
Signed-off-by: default avatarNeeraj Upadhyay <neeraju@codeaurora.org>
parent b9483eb8
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -487,6 +487,14 @@ config QCOM_DYN_MINIDUMP_STACK
	  where CPU is unable to register it from IPI_CPU_STOP. The stack data
	  can be used to unwind stack frames.

config QCOM_MINIDUMP_FTRACE
	bool "QCOM Minidump Support"
	depends on QGKI && QCOM_MINIDUMP
	help
	  This enables ftrace buffer registration in minidump table.
	  This allows dumping ftrace buffer content as part of
	  minidump dumps.

config MINIDUMP_MAX_ENTRIES
	int "Minidump Maximum num of entries"
	default 200
+51 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@
#include <asm/stacktrace.h>
#include <linux/mm.h>
#include <linux/ratelimit.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/sched/task.h>
#include <linux/suspend.h>
#include <linux/vmalloc.h>
@@ -54,6 +56,13 @@ static struct md_suspend_context_data md_suspend_context;

static bool is_vmap_stack __read_mostly;

#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
#define MD_FTRACE_BUF_SIZE	SZ_2M

static char *md_ftrace_buf_addr;
static size_t md_ftrace_buf_current;
#endif

static void __init register_log_buf(void)
{
	char **log_bufp;
@@ -489,6 +498,45 @@ static void register_irq_stack(void)
static inline void register_irq_stack(void) {}
#endif

#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
void minidump_add_trace_event(char *buf, size_t size)
{
	char *addr;

	if (!READ_ONCE(md_ftrace_buf_addr) ||
	    (size > (size_t)MD_FTRACE_BUF_SIZE))
		return;

	if ((md_ftrace_buf_current + size) > (size_t)MD_FTRACE_BUF_SIZE)
		md_ftrace_buf_current = 0;
	addr = md_ftrace_buf_addr + md_ftrace_buf_current;
	memcpy(addr, buf, size);
	md_ftrace_buf_current += size;
}

static void md_register_trace_buf(void)
{
	struct md_region md_entry;
	void *buffer_start;

	buffer_start = kmalloc(MD_FTRACE_BUF_SIZE, GFP_KERNEL);

	if (!buffer_start)
		return;

	strlcpy(md_entry.name, "KFTRACE", sizeof(md_entry.name));
	md_entry.virt_addr = (uintptr_t)buffer_start;
	md_entry.phys_addr = virt_to_phys(buffer_start);
	md_entry.size = MD_FTRACE_BUF_SIZE;
	if (msm_minidump_add_region(&md_entry) < 0)
		pr_err("Failed to add ftrace buffer entry in Minidump\n");

	/* Complete registration before adding enteries */
	smp_mb();
	WRITE_ONCE(md_ftrace_buf_addr, buffer_start);
}
#endif

static int __init msm_minidump_log_init(void)
{
	register_kernel_sections();
@@ -499,6 +547,9 @@ static int __init msm_minidump_log_init(void)
	register_suspend_context();
#endif
	register_log_buf();
#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
	md_register_trace_buf();
#endif
	return 0;
}
late_initcall(msm_minidump_log_init);
+7 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */

#ifndef __MINIDUMP_H
@@ -56,5 +56,11 @@ static inline int msm_minidump_remove_region(const struct md_region *entry)
}
static inline bool msm_minidump_enabled(void) { return false; }
static inline void dump_stack_minidump(u64 sp) {}
static inline void add_trace_event(char *buf, size_t size) {}
#endif
#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
extern void minidump_add_trace_event(char *buf, size_t size);
#else
static inline void minidump_add_trace_event(char *buf, size_t size) {}
#endif
#endif
+77 −1
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <linux/percpu.h>
#include <linux/splice.h>
#include <linux/kdebug.h>
#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/mount.h>
#include <linux/rwsem.h>
@@ -47,6 +48,8 @@
#include <linux/sched/rt.h>
#include <linux/coresight-stm.h>

#include <soc/qcom/minidump.h>

#include "trace.h"
#include "trace_output.h"

@@ -119,7 +122,13 @@ cpumask_var_t __read_mostly tracing_buffer_mask;
 * Set 2 if you want to dump the buffer of the CPU that triggered oops
 */

#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
enum ftrace_dump_mode ftrace_dump_on_oops = DUMP_ALL;
static bool minidump_ftrace_in_oops;
static bool minidump_ftrace_dump = true;
#else
enum ftrace_dump_mode ftrace_dump_on_oops;
#endif

/* When set, tracing will stop when a WARN*() is hit */
int __disable_trace_on_warning;
@@ -8842,11 +8851,32 @@ static __init int tracer_init_tracefs(void)
	return 0;
}

#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
static bool trace_oops_enter(void)
{
	if (minidump_ftrace_in_oops)
		return true;
	minidump_ftrace_in_oops = true;
	return false;
}

static void trace_oops_exit(void)
{
	minidump_ftrace_in_oops = false;
}
#else
static bool trace_oops_enter(void) { return false; }
static void trace_oops_exit(void) { }
#endif

static int trace_panic_handler(struct notifier_block *this,
			       unsigned long event, void *unused)
{
	if (trace_oops_enter())
		return NOTIFY_OK;
	if (ftrace_dump_on_oops)
		ftrace_dump(ftrace_dump_on_oops);
	trace_oops_exit();
	return NOTIFY_OK;
}

@@ -8860,6 +8890,8 @@ static int trace_die_handler(struct notifier_block *self,
			     unsigned long val,
			     void *data)
{
	if (trace_oops_enter())
		return NOTIFY_OK;
	switch (val) {
	case DIE_OOPS:
		if (ftrace_dump_on_oops)
@@ -8868,6 +8900,7 @@ static int trace_die_handler(struct notifier_block *self,
	default:
		break;
	}
	trace_oops_exit();
	return NOTIFY_OK;
}

@@ -8889,6 +8922,19 @@ static struct notifier_block trace_die_notifier = {
 */
#define KERN_TRACE		KERN_EMERG

#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
static void trace_dump_seq_buffer(struct trace_seq *s)
{
	if (minidump_ftrace_in_oops && minidump_ftrace_dump)
		minidump_add_trace_event(s->buffer, s->seq.len);
}
#else
static void trace_dump_seq_buffer(struct trace_seq *s)
{
	printk(KERN_TRACE "%s", s->buffer);
}
#endif

void
trace_printk_seq(struct trace_seq *s)
{
@@ -8907,7 +8953,7 @@ trace_printk_seq(struct trace_seq *s)
	/* should be zero ended, but we are paranoid. */
	s->buffer[s->seq.len] = 0;

	printk(KERN_TRACE "%s", s->buffer);
	trace_dump_seq_buffer(s);

	trace_seq_init(s);
}
@@ -8931,6 +8977,33 @@ void trace_init_global_iter(struct trace_iterator *iter)
		iter->iter_flags |= TRACE_FILE_TIME_IN_NS;
}

#ifdef CONFIG_QCOM_MINIDUMP_FTRACE
static void trace_check_size(struct trace_iterator iter, int cpu)
{
	unsigned long buffer_size;

	if (!minidump_ftrace_dump)
		return;
	buffer_size = ring_buffer_size(iter.tr->trace_buffer.buffer, cpu);
	if (buffer_size > (SZ_256K + PAGE_SIZE)) {
		printk(KERN_TRACE "Skip md ftrace buffer dump for: %#lx\n",
			buffer_size);
		minidump_ftrace_dump = false;
	}
}

static bool trace_skip_ftrace_dump(void)
{
	return !minidump_ftrace_dump;
}
#else
static void trace_check_size(struct trace_iterator iter, int cpu) { }
static bool trace_skip_ftrace_dump(void)
{
	return false;
}
#endif

void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
{
	/* use static because iter can be a bit big for the stack */
@@ -8965,6 +9038,7 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)

	for_each_tracing_cpu(cpu) {
		atomic_inc(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled);
		trace_check_size(iter, cpu);
	}

	old_userobj = tr->trace_flags & TRACE_ITER_SYM_USEROBJ;
@@ -8972,6 +9046,8 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
	/* don't look at user memory in panic mode */
	tr->trace_flags &= ~TRACE_ITER_SYM_USEROBJ;

	if (trace_skip_ftrace_dump())
		goto out_enable;
	switch (oops_dump_mode) {
	case DUMP_ALL:
		iter.cpu_file = RING_BUFFER_ALL_CPUS;