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: Neeraj Upadhyay <neeraju@codeaurora.org>
This commit is contained in:
Neeraj Upadhyay 2020-07-02 18:44:31 +05:30
commit 52a0d3dcb7
4 changed files with 143 additions and 2 deletions

View file

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

View file

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

View file

@ -1,6 +1,6 @@
/* 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

View file

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