Ver Fonte

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
Yu Wang há 3 anos atrás
pai
commit
56ebf2707b
3 ficheiros alterados com 197 adições e 12 exclusões
  1. 24 0
      qdf/inc/qdf_trace.h
  2. 59 12
      qdf/linux/src/i_qdf_trace.h
  3. 114 0
      qdf/linux/src/qdf_trace.c

+ 24 - 0
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

+ 59 - 12
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 <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
  * 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 <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>
 #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;
 }
-#else
+
 static inline void
-__qdf_minidump_log(void *start_addr,
-		   const size_t size, const char *name) {}
+__qdf_minidump_init(void)
+{
+}
+
 static inline void
-__qdf_minidump_remove(void *start_addr,
-		      const size_t size, const char *name) {}
+__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_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 */

+ 114 - 0
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