Browse Source

qcacmn: Add nbuf map/unmap history tracking

An nbuf free history circular buffer already exists for tracking recent
nbuf frees in the system. This is very useful for debugging tricky nbuf
related failures in the driver. In addition to tracking frees, track
alloc, map, and unmap nbuf events as well.

Change-Id: I253d454d689deb8328b3636e92063e9d33ea1a52
CRs-Fixed: 2170365
Dustin Brown 7 years ago
parent
commit
976a7f6f6e
2 changed files with 90 additions and 102 deletions
  1. 6 33
      qdf/inc/qdf_nbuf.h
  2. 84 69
      qdf/linux/src/qdf_nbuf.c

+ 6 - 33
qdf/inc/qdf_nbuf.h

@@ -1026,47 +1026,20 @@ void qdf_net_buf_debug_delete_node(qdf_nbuf_t net_buf);
 void qdf_net_buf_debug_acquire_skb(qdf_nbuf_t net_buf,
 			uint8_t *file_name, uint32_t line_num);
 void qdf_net_buf_debug_release_skb(qdf_nbuf_t net_buf);
-void qdf_net_buf_free_debug_add(qdf_nbuf_t net_buf, uint8_t *file_name,
-				uint32_t line_num);
 
 /* nbuf allocation rouines */
 
-#define qdf_nbuf_alloc(d, s, r, a, p)			\
+#define qdf_nbuf_alloc(d, s, r, a, p) \
 	qdf_nbuf_alloc_debug(d, s, r, a, p, __FILE__, __LINE__)
-static inline qdf_nbuf_t
-qdf_nbuf_alloc_debug(qdf_device_t osdev, qdf_size_t size, int reserve,
-		int align, int prio, uint8_t *file_name,
-		uint32_t line_num)
-{
-	qdf_nbuf_t net_buf;
-
-	net_buf = __qdf_nbuf_alloc(osdev, size, reserve, align, prio);
-
-	/* Store SKB in internal QDF tracking table */
-	if (qdf_likely(net_buf))
-		qdf_net_buf_debug_add_node(net_buf, size, file_name, line_num);
 
-	return net_buf;
-}
+qdf_nbuf_t qdf_nbuf_alloc_debug(qdf_device_t osdev, qdf_size_t size,
+				int reserve, int align, int prio,
+				uint8_t *file, uint32_t line);
 
-#define qdf_nbuf_free(d)			\
+#define qdf_nbuf_free(d) \
 	qdf_nbuf_free_debug(d, __FILE__, __LINE__)
-static inline void qdf_nbuf_free_debug(qdf_nbuf_t net_buf,
-				       uint8_t *file_name, uint32_t line_num)
-{
-	if (qdf_nbuf_is_tso(net_buf) &&
-			qdf_nbuf_get_users(net_buf) > 1)
-		goto free_buf;
 
-	/* Remove SKB from internal QDF tracking table */
-	if (qdf_likely(net_buf)) {
-		qdf_net_buf_free_debug_add(net_buf, file_name, line_num);
-		qdf_net_buf_debug_delete_node(net_buf);
-	}
-
-free_buf:
-	__qdf_nbuf_free(net_buf);
-}
+void qdf_nbuf_free_debug(qdf_nbuf_t nbuf, uint8_t *file, uint32_t line);
 
 #define qdf_nbuf_clone(buf)     \
 	qdf_nbuf_clone_debug(buf, __FILE__, __LINE__)

+ 84 - 69
qdf/linux/src/qdf_nbuf.c

@@ -470,6 +470,50 @@ void __qdf_nbuf_free(struct sk_buff *skb)
 EXPORT_SYMBOL(__qdf_nbuf_free);
 
 #ifdef MEMORY_DEBUG
+enum qdf_nbuf_event_type {
+	QDF_NBUF_ALLOC,
+	QDF_NBUF_FREE,
+	QDF_NBUF_MAP,
+	QDF_NBUF_UNMAP,
+};
+
+struct qdf_nbuf_event {
+	qdf_nbuf_t nbuf;
+	const char *file;
+	uint32_t line;
+	enum qdf_nbuf_event_type type;
+	uint64_t timestamp;
+};
+
+#define QDF_NBUF_HISTORY_SIZE 4096
+static qdf_atomic_t qdf_nbuf_history_index;
+static struct qdf_nbuf_event qdf_nbuf_history[QDF_NBUF_HISTORY_SIZE];
+
+static int32_t qdf_nbuf_circular_index_next(qdf_atomic_t *index, int size)
+{
+	int32_t next = qdf_atomic_inc_return(index);
+
+	if (next == size)
+		qdf_atomic_sub(size, index);
+
+	return next % size;
+}
+
+static void
+qdf_nbuf_history_add(qdf_nbuf_t nbuf, const char *file, uint32_t line,
+		     enum qdf_nbuf_event_type type)
+{
+	int32_t idx = qdf_nbuf_circular_index_next(&qdf_nbuf_history_index,
+						   QDF_NBUF_HISTORY_SIZE);
+	struct qdf_nbuf_event *event = &qdf_nbuf_history[idx];
+
+	event->nbuf = nbuf;
+	event->file = file;
+	event->line = line;
+	event->type = type;
+	event->timestamp = qdf_get_log_timestamp();
+}
+
 #define QDF_NBUF_MAP_HT_BITS 10 /* 1024 buckets */
 static DECLARE_HASHTABLE(qdf_nbuf_map_ht, QDF_NBUF_MAP_HT_BITS);
 static qdf_spinlock_t qdf_nbuf_map_lock;
@@ -569,6 +613,8 @@ qdf_nbuf_track_map(qdf_nbuf_t nbuf, const char *file, uint32_t line)
 	hash_add(qdf_nbuf_map_ht, &meta->node, (size_t)nbuf);
 	qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
 
+	qdf_nbuf_history_add(nbuf, file, line, QDF_NBUF_MAP);
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -594,6 +640,8 @@ qdf_nbuf_untrack_map(qdf_nbuf_t nbuf, const char *file, uint32_t line)
 	qdf_spin_unlock_irqrestore(&qdf_nbuf_map_lock);
 
 	qdf_mem_free(meta);
+
+	qdf_nbuf_history_add(nbuf, file, line, QDF_NBUF_UNMAP);
 }
 
 QDF_STATUS qdf_nbuf_map_debug(qdf_device_t osdev,
@@ -1606,30 +1654,6 @@ static uint32_t qdf_net_buf_track_max_used;
 static uint32_t qdf_net_buf_track_max_free;
 static uint32_t qdf_net_buf_track_max_allocated;
 
-/**
- * struct qdf_nbuf_free_track_t - Network buffer free track structure
- * @p_next: Pointer to next
- * @net_buf: Pointer to network buffer
- * @file_name: File name
- * @line_num: Line number
- * @time: Time stamp
- */
-struct qdf_nbuf_free_track_t {
-	struct qdf_nbuf_track_t *p_next;
-	qdf_nbuf_t net_buf;
-	uint8_t *file_name;
-	uint32_t line_num;
-	uint64_t time;
-};
-
-struct qdf_nbuf_free_record_t {
-	struct qdf_nbuf_free_track_t gp_qdf_netbuf_free_tbl[
-					QDF_NET_BUF_TRACK_MAX_SIZE];
-	qdf_atomic_t count;
-};
-
-static struct qdf_nbuf_free_record_t qdf_nbuf_free_record_info;
-
 /**
  * update_max_used() - update qdf_net_buf_track_max_used tracking variable
  *
@@ -1880,6 +1904,8 @@ void qdf_net_buf_debug_init(void)
 {
 	uint32_t i;
 
+	qdf_atomic_set(&qdf_nbuf_history_index, -1);
+
 	qdf_nbuf_map_tracking_init();
 	qdf_nbuf_track_memory_manager_create();
 
@@ -1934,51 +1960,6 @@ void qdf_net_buf_debug_exit(void)
 }
 EXPORT_SYMBOL(qdf_net_buf_debug_exit);
 
-/**
- * qdf_nbuf_free_dbg_get_index() - Get the next record index
- * @tbl_index: atomic index variable to increment
- * @size: array size of the circular buffer
- *
- * Return: array index
- */
-static int qdf_nbuf_free_dbg_get_index(qdf_atomic_t *tbl_index, int size)
-{
-	int index = qdf_atomic_inc_return(tbl_index);
-
-	if (index == size)
-		qdf_atomic_sub(size, tbl_index);
-
-	while (index >= size)
-		index -= size;
-
-	return index;
-}
-
-/**
- * qdf_netbuf_free_debug_add() - Add netbuff free info to the debug
- * @netbuf: pointer to network buffer
- * @file_name: fie name from where net buff is freed
- * @line_num: line number
- *
- * Return: none
- */
-void qdf_net_buf_free_debug_add(qdf_nbuf_t net_buf, uint8_t *file_name,
-				uint32_t line_num)
-{
-	struct qdf_nbuf_free_track_t *p_node;
-	uint16_t index;
-
-	index = qdf_nbuf_free_dbg_get_index(&qdf_nbuf_free_record_info.count,
-					  QDF_NET_BUF_TRACK_MAX_SIZE);
-
-	p_node = &qdf_nbuf_free_record_info.gp_qdf_netbuf_free_tbl[index];
-	p_node->net_buf = net_buf;
-	p_node->file_name = file_name;
-	p_node->line_num = line_num;
-	p_node->time = qdf_get_log_timestamp();
-}
-EXPORT_SYMBOL(qdf_net_buf_free_debug_add);
-
 /**
  * qdf_net_buf_debug_hash() - hash network buffer pointer
  *
@@ -2165,6 +2146,40 @@ void qdf_net_buf_debug_release_skb(qdf_nbuf_t net_buf)
 }
 EXPORT_SYMBOL(qdf_net_buf_debug_release_skb);
 
+qdf_nbuf_t qdf_nbuf_alloc_debug(qdf_device_t osdev, qdf_size_t size,
+				int reserve, int align, int prio,
+				uint8_t *file, uint32_t line)
+{
+	qdf_nbuf_t nbuf;
+
+	nbuf = __qdf_nbuf_alloc(osdev, size, reserve, align, prio);
+
+	/* Store SKB in internal QDF tracking table */
+	if (qdf_likely(nbuf)) {
+		qdf_net_buf_debug_add_node(nbuf, size, file, line);
+		qdf_nbuf_history_add(nbuf, file, line, QDF_NBUF_ALLOC);
+	}
+
+	return nbuf;
+}
+qdf_export_symbol(qdf_nbuf_alloc_debug);
+
+void qdf_nbuf_free_debug(qdf_nbuf_t nbuf, uint8_t *file, uint32_t line)
+{
+	if (qdf_nbuf_is_tso(nbuf) && qdf_nbuf_get_users(nbuf) > 1)
+		goto free_buf;
+
+	/* Remove SKB from internal QDF tracking table */
+	if (qdf_likely(nbuf)) {
+		qdf_net_buf_debug_delete_node(nbuf);
+		qdf_nbuf_history_add(nbuf, file, line, QDF_NBUF_FREE);
+	}
+
+free_buf:
+	__qdf_nbuf_free(nbuf);
+}
+qdf_export_symbol(qdf_nbuf_free_debug);
+
 #endif /*MEMORY_DEBUG */
 #if defined(FEATURE_TSO)