Browse Source

qcacmn: Support live dump feature

Target firmware Assert are at times related to Host Driver
interaction with the firmware. To debug these issues, host
driver dump is mandatory. The feature provides memory dump
of data structures registed with this framework. The dumps
are saved which can later be loaded using gdb and analysed.

Add apis support for the feature.

Change-Id: I41c5126a49a698969f24b17187f78f4b4db7a51e
CRs-Fixed: 2478139
Uraj Sasan 5 years ago
parent
commit
37b2c4d198
5 changed files with 386 additions and 0 deletions
  1. 2 0
      qdf/inc/qdf_types.h
  2. 2 0
      qdf/linux/src/qdf_trace.c
  3. 114 0
      utils/qld/inc/qld_api.h
  4. 68 0
      utils/qld/inc/qld_priv.h
  5. 200 0
      utils/qld/src/qld.c

+ 2 - 0
qdf/inc/qdf_types.h

@@ -372,6 +372,7 @@ typedef bool (*qdf_irqlocked_func_t)(void *);
  * @QDF_MODULE_ID_TX_CAPTURE: Tx capture enhancement feature ID
  * @QDF_MODULE_ID_INTEROP_ISSUES_AP: interop issues ap module ID
  * @QDF_MODULE_ID_BLACKLIST_MGR: Blacklist Manager module
+ * @QDF_MODULE_ID_QLD: QCA Live Debug module ID
  * @QDF_MODULE_ID_ANY: anything
  * @QDF_MODULE_ID_MAX: Max place holder module ID
  */
@@ -488,6 +489,7 @@ typedef enum {
 	QDF_MODULE_ID_TX_CAPTURE,
 	QDF_MODULE_ID_INTEROP_ISSUES_AP,
 	QDF_MODULE_ID_BLACKLIST_MGR,
+	QDF_MODULE_ID_QLD,
 	QDF_MODULE_ID_ANY,
 	QDF_MODULE_ID_MAX,
 } QDF_MODULE_ID;

+ 2 - 0
qdf/linux/src/qdf_trace.c

@@ -2710,6 +2710,7 @@ struct category_name_info g_qdf_category_name[MAX_SUPPORTED_CATEGORY] = {
 	[QDF_MODULE_ID_TX_CAPTURE] = {"TX_CAPTURE_ENHANCE"},
 	[QDF_MODULE_ID_INTEROP_ISSUES_AP] = {"INTEROP_ISSUES_AP"},
 	[QDF_MODULE_ID_BLACKLIST_MGR] = {"blm"},
+	[QDF_MODULE_ID_QLD] = {"QLD"},
 	[QDF_MODULE_ID_ANY] = {"ANY"},
 };
 qdf_export_symbol(g_qdf_category_name);
@@ -3168,6 +3169,7 @@ static void set_default_trace_levels(struct category_info *cinfo)
 		[QDF_MODULE_ID_TX_CAPTURE] = QDF_TRACE_LEVEL_NONE,
 		[QDF_MODULE_ID_INTEROP_ISSUES_AP] = QDF_TRACE_LEVEL_NONE,
 		[QDF_MODULE_ID_BLACKLIST_MGR] = QDF_TRACE_LEVEL_NONE,
+		[QDF_MODULE_ID_QLD] = QDF_TRACE_LEVEL_ERROR,
 		[QDF_MODULE_ID_ANY] = QDF_TRACE_LEVEL_INFO,
 	};
 

+ 114 - 0
utils/qld/inc/qld_api.h

@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: qld_api.h
+ * QLD: This file provides public exposed functions
+ */
+
+#ifndef _QLD_API_H_
+#define _QLD_API_H_
+
+#define QLD_MAX_NAME    48
+
+/**
+ * struct qld_entry - Individual entry in qld_event
+ * @addr: Start address of object to dump
+ * @size: Size of memory dump
+ * @name: Name of memory dump
+ */
+struct qld_entry {
+	uint64_t addr;
+	size_t size;
+	char name[QLD_MAX_NAME];
+};
+
+/**
+ * typedef qld_iter_func - qld callback function
+ * @req: opaque pointer
+ * @qld_entry: qld_entry
+ *
+ * Return: 0 - OK -EINVAL - On failure
+ */
+typedef int (*qld_iter_func)(void *req, struct qld_entry *entry);
+
+/**
+ * qld_iterate_list() - qld list iteration routine
+ * @gen_table: callback function to genrate table
+ * @req: opaque request
+ *
+ * Return: 0 - OK -EINVAL - On failure
+ */
+int qld_iterate_list(qld_iter_func gen_table, void *req);
+
+/**
+ * qld_register() - Register qld for the given address
+ * @addr: starting address the dump
+ * @size: size of memory to dump
+ * @name: name identifier of dump
+ *
+ * Return: 0 - OK -EINVAL -ENOMEM - On failure
+ */
+int qld_register(void *addr, size_t size, char *name);
+
+/**
+ * qld_unregister() - Un-register qld for the given address
+ * @addr: starting address the dump
+ *
+ * Return: 0 - OK -EINVAL - On failure
+ */
+int qld_unregister(void *addr);
+
+/**
+ * qld_list_init() - Initialize qld list
+ * @max_list: maximum size list supports
+ *
+ * Return: 0 - OK -EINVAL -ENOMEM - On failure
+ */
+int qld_list_init(uint32_t max_list);
+
+/**
+ * qld_list_delete() - empty qld list
+ *
+ * Return: 0 - OK -EINVAL - On failure
+ */
+int qld_list_delete(void);
+
+/**
+ * qld_list_deinit() - De-initialize qld list
+ *
+ * Return: 0 - OK -EINVAL - On failure
+ */
+int qld_list_deinit(void);
+
+/**
+ * qld_get_list_count () - get size of qld list
+ * @list_count: list_count to set
+ *
+ * Return: 0 - OK -EINVAL - On failure
+ */
+int qld_get_list_count(uint32_t *list_count);
+
+/**
+ * is_qld_enable() - check if qld feature is set
+ *
+ * Return: true on success, false on failure
+ */
+bool is_qld_enable(void);
+
+#endif /* _QLD_API_H_ */

+ 68 - 0
utils/qld/inc/qld_priv.h

@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: qld_priv.h
+ * QLD: This file provies Private functions for qld
+ */
+
+#ifndef _QLD_PRIV_H_
+#define _QLD_PRIV_H_
+
+#include <qdf_lock.h>
+#include <qdf_list.h>
+#include <qld_api.h>
+
+#define qld_alert(format, args...) \
+		QDF_TRACE_FATAL(QDF_MODULE_ID_QLD, format, ## args)
+
+#define qld_err(format, args...) \
+		QDF_TRACE_ERROR(QDF_MODULE_ID_QLD, format, ## args)
+
+#define qld_warn(format, args...) \
+		QDF_TRACE_WARN(QDF_MODULE_ID_QLD, format, ## args)
+
+#define qld_info(format, args...) \
+		QDF_TRACE_INFO(QDF_MODULE_ID_QLD, format, ## args)
+
+#define qld_debug(format, args...) \
+		QDF_TRACE_DEBUG(QDF_MODULE_ID_QLD, format, ## args)
+
+/**
+ * struct qld_list_handle - Top level qld structure
+ * @qld_lock:      Spinlock for structure
+ * @qld_list:      linked list for linking
+ * @qld_max_list:  maximum list size
+ */
+struct qld_list_handle {
+	qdf_spinlock_t qld_lock;
+	qdf_list_t qld_list;
+	uint32_t qld_max_list;
+};
+
+/**
+ * struct qld_node - qld node
+ * @node:          single node of linked list
+ * @entry:         single qld_entry in list
+ */
+struct qld_node {
+	qdf_list_node_t node;
+	struct qld_entry entry;
+};
+
+#endif /*_QLD_PRIV_H_*/

+ 200 - 0
utils/qld/src/qld.c

@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: qld
+ * QLD: main file of QCA Live Dump (QLD)
+ */
+
+#include "qld_priv.h"
+#include "qld_api.h"
+#include "qdf_module.h"
+
+/* Handle for qld structure */
+static struct qld_list_handle *qld_handle;
+
+bool is_qld_enable(void)
+{
+	if (!qld_handle)
+		return false;
+
+	return true;
+}
+
+qdf_export_symbol(is_qld_enable);
+
+int qld_list_init(uint32_t max_list)
+{
+	if (!max_list)
+		return -EINVAL;
+
+	qld_handle = qdf_mem_malloc(sizeof(*qld_handle));
+
+	if (!qld_handle)
+		return -ENOMEM;
+
+	qdf_spinlock_create(&qld_handle->qld_lock);
+	qld_handle->qld_max_list = max_list;
+	qdf_list_create(&qld_handle->qld_list, qld_handle->qld_max_list);
+	qld_debug("LIST init with max size of %u", qld_handle->qld_max_list);
+	return 0;
+}
+
+qdf_export_symbol(qld_list_init);
+
+int qld_list_deinit(void)
+{
+	if (!qld_handle) {
+		qld_err("Handle NULL");
+		return -EINVAL;
+	}
+	/* Delete the list */
+	qld_list_delete();
+	qdf_list_destroy(&qld_handle->qld_list);
+	qdf_spinlock_destroy(&qld_handle->qld_lock);
+	qdf_mem_free(qld_handle);
+	qld_handle = NULL;
+	qld_debug("LIST De-initialized");
+	return 0;
+}
+
+qdf_export_symbol(qld_list_deinit);
+
+int qld_list_delete(void)
+{
+	struct qld_node *qld;
+	qdf_list_node_t *node = NULL;
+	qdf_list_t *list;
+
+	if (!qld_handle) {
+		qld_err("Handle NULL");
+		return -EINVAL;
+	}
+	list = &qld_handle->qld_list;
+	qdf_spinlock_acquire(&qld_handle->qld_lock);
+	/* Check and remove the elements of list */
+	while (qdf_list_remove_front(list, &node) == QDF_STATUS_SUCCESS) {
+		qld = qdf_container_of(node, struct qld_node, node);
+		qdf_mem_free(qld);
+	}
+	qdf_spinlock_release(&qld_handle->qld_lock);
+	qld_debug("LIST Emptied");
+	return 0;
+}
+
+qdf_export_symbol(qld_list_delete);
+
+int qld_register(void *addr, size_t size, char *name)
+{
+	struct qld_node *qld;
+	uint32_t list_count = 0;
+
+	if (!qld_handle || !addr) {
+		qld_err("Handle or address is NULL");
+		return -EINVAL;
+	}
+
+	if ((qld_get_list_count(&list_count) != 0)) {
+		qdf_err("QLD: Invalid list count");
+		return -EINVAL;
+	}
+	if (list_count >= qld_handle->qld_max_list) {
+		qld_err("List full,reg failed.Increase list size");
+		return -EINVAL;
+	}
+	/* Check if data is already registered */
+	qdf_spinlock_acquire(&qld_handle->qld_lock);
+	qdf_list_for_each(&qld_handle->qld_list, qld, node) {
+		if (qld->entry.addr == (uintptr_t)addr) {
+			qld_err("%s already registered", qld->entry.name);
+			qdf_spinlock_release(&qld_handle->qld_lock);
+			return -EINVAL;
+		}
+	}
+	qdf_spinlock_release(&qld_handle->qld_lock);
+	qld = qdf_mem_malloc(sizeof(*qld));
+	if (!qld)
+		return -ENOMEM;
+
+	qld_debug("Insert addr=%pK size=%zu name=%s", (void *)addr, size, name);
+	qdf_spinlock_acquire(&qld_handle->qld_lock);
+	qld->entry.addr =  (uintptr_t)addr;
+	qld->entry.size = size;
+	qdf_snprintf(qld->entry.name, sizeof(qld->entry.name), "%s", name);
+	qdf_list_insert_front(&qld_handle->qld_list, &qld->node);
+	qdf_spinlock_release(&qld_handle->qld_lock);
+	return 0;
+}
+
+qdf_export_symbol(qld_register);
+
+int qld_unregister(void *addr)
+{
+	struct qld_node *qld  = NULL;
+
+	if (!qld_handle || !addr) {
+		qld_err("Handle or address is NULL");
+		return -EINVAL;
+	}
+
+	qdf_spinlock_acquire(&qld_handle->qld_lock);
+	qdf_list_for_each(&qld_handle->qld_list, qld, node) {
+		if (qld->entry.addr == (uintptr_t)addr)
+			break;
+	}
+	qdf_list_remove_node(&qld_handle->qld_list, &qld->node);
+	qld_debug("Delete name=%s, size=%zu", qld->entry.name, qld->entry.size);
+	qdf_mem_free(qld);
+	qdf_spinlock_release(&qld_handle->qld_lock);
+	return 0;
+}
+
+qdf_export_symbol(qld_unregister);
+
+int qld_iterate_list(qld_iter_func gen_table, void *qld_req)
+{
+	struct qld_node *qld  = NULL;
+
+	if (!qld_handle)
+		return -EINVAL;
+
+	if (!qld_req || !gen_table) {
+		qld_err("req buffer or func is NULL %s", __func__);
+		return -EINVAL;
+	}
+	qdf_spinlock_acquire(&qld_handle->qld_lock);
+	qdf_list_for_each(&qld_handle->qld_list, qld, node) {
+		(gen_table)(qld_req, &qld->entry);
+	}
+	qdf_spinlock_release(&qld_handle->qld_lock);
+	return 0;
+}
+
+qdf_export_symbol(qld_iterate_list);
+
+int qld_get_list_count(uint32_t *list_count)
+{
+	if (!qld_handle) {
+		qld_err("Handle NULL");
+		return -EINVAL;
+	}
+	*list_count = qld_handle->qld_list.count;
+	return 0;
+}
+
+qdf_export_symbol(qld_get_list_count);