diff --git a/qdf/inc/qdf_trace.h b/qdf/inc/qdf_trace.h index dcd450f2f2..c14ab747ff 100644 --- a/qdf/inc/qdf_trace.h +++ b/qdf/inc/qdf_trace.h @@ -1696,6 +1696,30 @@ void qdf_logging_exit(void); */ 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 * @start_addr: Start address of the memory to be dumped diff --git a/qdf/linux/src/i_qdf_trace.h b/qdf/linux/src/i_qdf_trace.h index 88e72ee317..a8b835ef73 100644 --- a/qdf/linux/src/i_qdf_trace.h +++ b/qdf/linux/src/i_qdf_trace.h @@ -27,6 +27,13 @@ #if !defined(__I_QDF_TRACE_H) #define __I_QDF_TRACE_H +/* older kernels have a bug in kallsyms, so ensure module.h is included */ +#include +#include +#ifdef CONFIG_QCA_MINIDUMP +#include +#endif + /* * The CONFIG_QCOM_MINIDUMP feature can only be used * beginning with kernel version msm-4.19 since that is @@ -34,15 +41,12 @@ */ #if IS_ENABLED(CONFIG_QCOM_MINIDUMP) && \ (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 #endif -/* older kernels have a bug in kallsyms, so ensure module.h is included */ -#include -#include -#ifdef CONFIG_QCA_MINIDUMP -#include -#endif -#ifdef WLAN_QCOM_MINIDUMP + #include #endif @@ -469,6 +473,16 @@ static inline void __qdf_bug(void) #endif #ifdef CONFIG_QCA_MINIDUMP +static inline void +__qdf_minidump_init(void) +{ +} + +static inline void +__qdf_minidump_deinit(void) +{ +} + static inline void __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); } + #elif defined(WLAN_QCOM_MINIDUMP) #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); 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 -static inline void -__qdf_minidump_log(void *start_addr, - 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_init(void) +{ +} + +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 /* __I_QDF_TRACE_H */ diff --git a/qdf/linux/src/qdf_trace.c b/qdf/linux/src/qdf_trace.c index 9df031d568..c8bf7f2388 100644 --- a/qdf/linux/src/qdf_trace.c +++ b/qdf/linux/src/qdf_trace.c @@ -4602,3 +4602,117 @@ qdf_export_symbol(__qdf_bug); #endif /* CONFIG_SLUB_DEBUG */ #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