qcacmn: add support for VA based minidump

Before GKI enforcement and driver modulization, vendor drivers’
data section was part of vmlinux and they were registered with
minidump and collected when crash happens.
With GKI, vendor modules’ data section is part of va-malloc
region, which is virtually contiguous but physically not. It
is difficult to fit into the existing PA based minidump framework.
New VA-minidump driver allocates some CMA region which is registered
as one entry in minidump infrastructure. During panic, the driver
creates an ELF which is based on VA, copies it into that CMA region,
and finally it gets dumped as md_KVA_DUMP.bin.
This change makes use of this VA-minidump framework and implement
the APIs accordingly.

Change-Id: Ib1282990c91408bcd4e68365afe694830b90d0ac
CRs-Fixed: 3232060
This commit is contained in:
Yu Wang
2022-06-27 17:30:49 +08:00
committed by Madan Koyyalamudi
parent b67f4a7465
commit 56ebf2707b
3 changed files with 198 additions and 13 deletions

View File

@@ -1696,6 +1696,30 @@ void qdf_logging_exit(void);
*/ */
int qdf_sprint_symbol(char *buffer, void *addr); int qdf_sprint_symbol(char *buffer, void *addr);
/**
* qdf_minidump_init() - Initialize minidump functionality
*
*
* Return: void
*/
static inline
void qdf_minidump_init(void)
{
__qdf_minidump_init();
}
/**
* qdf_minidump_deinit() - De-initialize minidump functionality
*
*
* Return: void
*/
static inline
void qdf_minidump_deinit(void)
{
__qdf_minidump_deinit();
}
/** /**
* qdf_minidump_log() - Log memory address to be included in minidump * qdf_minidump_log() - Log memory address to be included in minidump
* @start_addr: Start address of the memory to be dumped * @start_addr: Start address of the memory to be dumped

View File

@@ -27,6 +27,13 @@
#if !defined(__I_QDF_TRACE_H) #if !defined(__I_QDF_TRACE_H)
#define __I_QDF_TRACE_H #define __I_QDF_TRACE_H
/* older kernels have a bug in kallsyms, so ensure module.h is included */
#include <linux/module.h>
#include <linux/kallsyms.h>
#ifdef CONFIG_QCA_MINIDUMP
#include <linux/minidump_tlv.h>
#endif
/* /*
* The CONFIG_QCOM_MINIDUMP feature can only be used * The CONFIG_QCOM_MINIDUMP feature can only be used
* beginning with kernel version msm-4.19 since that is * beginning with kernel version msm-4.19 since that is
@@ -34,15 +41,12 @@
*/ */
#if IS_ENABLED(CONFIG_QCOM_MINIDUMP) && \ #if IS_ENABLED(CONFIG_QCOM_MINIDUMP) && \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
#if IS_ENABLED(CONFIG_QCOM_VA_MINIDUMP)
#define WLAN_QCOM_VA_MINIDUMP
#else
#define WLAN_QCOM_MINIDUMP #define WLAN_QCOM_MINIDUMP
#endif #endif
/* older kernels have a bug in kallsyms, so ensure module.h is included */
#include <linux/module.h>
#include <linux/kallsyms.h>
#ifdef CONFIG_QCA_MINIDUMP
#include <linux/minidump_tlv.h>
#endif
#ifdef WLAN_QCOM_MINIDUMP
#include <soc/qcom/minidump.h> #include <soc/qcom/minidump.h>
#endif #endif
@@ -469,6 +473,16 @@ static inline void __qdf_bug(void)
#endif #endif
#ifdef CONFIG_QCA_MINIDUMP #ifdef CONFIG_QCA_MINIDUMP
static inline void
__qdf_minidump_init(void)
{
}
static inline void
__qdf_minidump_deinit(void)
{
}
static inline void static inline void
__qdf_minidump_log(void *start_addr, size_t size, const char *name) __qdf_minidump_log(void *start_addr, size_t size, const char *name)
{ {
@@ -485,6 +499,7 @@ __qdf_minidump_remove(void *addr, size_t size, const char *name)
{ {
minidump_remove_segments((const uintptr_t)addr); minidump_remove_segments((const uintptr_t)addr);
} }
#elif defined(WLAN_QCOM_MINIDUMP) #elif defined(WLAN_QCOM_MINIDUMP)
#define MAX_WLAN_MINIDUMP_ENTRIES 4 #define MAX_WLAN_MINIDUMP_ENTRIES 4
@@ -566,12 +581,44 @@ __qdf_minidump_remove(void *start_addr, const size_t size,
msm_minidump_remove_region(&md_entry); msm_minidump_remove_region(&md_entry);
minidump_table[index] = NULL; minidump_table[index] = NULL;
} }
static inline void
__qdf_minidump_init(void)
{
}
static inline void
__qdf_minidump_deinit(void)
{
}
#elif defined(WLAN_QCOM_VA_MINIDUMP)
void __qdf_minidump_init(void);
void __qdf_minidump_deinit(void);
void __qdf_minidump_log(void *start_addr, size_t size, const char *name);
void __qdf_minidump_remove(void *addr, size_t size, const char *name);
#else #else
static inline void static inline
__qdf_minidump_log(void *start_addr, void __qdf_minidump_init(void)
const size_t size, const char *name) {} {
static inline void }
__qdf_minidump_remove(void *start_addr,
const size_t size, const char *name) {} static inline
void __qdf_minidump_deinit(void)
{
}
static inline
void __qdf_minidump_log(void *start_addr, size_t size, const char *name)
{
}
static inline
void __qdf_minidump_remove(void *addr, size_t size, const char *name)
{
}
#endif #endif
#endif /* __I_QDF_TRACE_H */ #endif /* __I_QDF_TRACE_H */

View File

@@ -4602,3 +4602,117 @@ qdf_export_symbol(__qdf_bug);
#endif /* CONFIG_SLUB_DEBUG */ #endif /* CONFIG_SLUB_DEBUG */
#endif /* PANIC_ON_BUG */ #endif /* PANIC_ON_BUG */
#ifdef WLAN_QCOM_VA_MINIDUMP
static bool qdf_va_md_initialized;
static qdf_list_t qdf_va_md_list;
static qdf_spinlock_t qdf_va_md_list_lock;
#define QDF_MINIDUMP_LIST_SIZE 128
struct qdf_va_md_entry {
qdf_list_node_t node;
struct va_md_entry data;
};
static int qdf_va_md_notif_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct qdf_va_md_entry *entry;
struct qdf_va_md_entry *next;
qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
qdf_list_for_each_del(&qdf_va_md_list, entry, next, node) {
qcom_va_md_add_region(&entry->data);
}
qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
return NOTIFY_OK;
}
static struct notifier_block qdf_va_md_notif_blk = {
.notifier_call = qdf_va_md_notif_handler,
.priority = INT_MAX,
};
void __qdf_minidump_init(void)
{
qdf_spinlock_create(&qdf_va_md_list_lock);
qdf_list_create(&qdf_va_md_list, QDF_MINIDUMP_LIST_SIZE);
qcom_va_md_register("qdf_va_md", &qdf_va_md_notif_blk);
qdf_va_md_initialized = true;
}
qdf_export_symbol(__qdf_minidump_init);
void __qdf_minidump_deinit(void)
{
struct qdf_va_md_entry *entry;
struct qdf_va_md_entry *next;
qdf_va_md_initialized = false;
qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
qdf_list_for_each_del(&qdf_va_md_list, entry, next, node) {
qdf_list_remove_node(&qdf_va_md_list, &entry->node);
qdf_mem_free(entry);
}
qdf_list_destroy(&qdf_va_md_list);
qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
qdf_spinlock_destroy(&qdf_va_md_list_lock);
}
qdf_export_symbol(__qdf_minidump_deinit);
void __qdf_minidump_log(void *start_addr, size_t size, const char *name)
{
struct qdf_va_md_entry *entry;
QDF_STATUS status;
if (!qdf_va_md_initialized)
return;
entry = qdf_mem_malloc(sizeof(*entry));
if (!entry) {
qdf_err("malloc failed for %s: %pK, %zu",
name, start_addr, size);
return;
}
qdf_str_lcopy(entry->data.owner, name, sizeof(entry->data.owner));
entry->data.vaddr = (unsigned long)start_addr;
entry->data.size = size;
qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
status = qdf_list_insert_front(&qdf_va_md_list, &entry->node);
qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
if (QDF_IS_STATUS_ERROR(status)) {
qdf_err("Failed to insert qdf va md entry, status %d", status);
qdf_mem_free(entry);
}
}
qdf_export_symbol(__qdf_minidump_log);
void __qdf_minidump_remove(void *addr, size_t size, const char *name)
{
struct qdf_va_md_entry *entry;
struct qdf_va_md_entry *next;
if (!qdf_va_md_initialized)
return;
qdf_spin_lock_irqsave(&qdf_va_md_list_lock);
qdf_list_for_each_del(&qdf_va_md_list, entry, next, node) {
if (entry->data.vaddr == (unsigned long)addr &&
entry->data.size == size &&
!qdf_str_cmp(entry->data.owner, name)) {
qdf_list_remove_node(&qdf_va_md_list, &entry->node);
qdf_mem_free(entry);
break;
}
}
qdf_spin_unlock_irqrestore(&qdf_va_md_list_lock);
}
qdf_export_symbol(__qdf_minidump_remove);
#endif