diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index b5fa4ea3e806..5f4be7fff9bb 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -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 diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c index 6ce6209ab6a2..06c7c5a8a2b0 100644 --- a/drivers/soc/qcom/minidump_log.c +++ b/drivers/soc/qcom/minidump_log.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -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); diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h index b6673b84ff5b..66eaa0ae42ed 100644 --- a/include/soc/qcom/minidump.h +++ b/include/soc/qcom/minidump.h @@ -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 diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index ee4eb5ed5d56..7f754e0109da 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,8 @@ #include #include +#include + #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;