xref: /wlan-driver/qca-wifi-host-cmn/umac/cp_stats/core/src/wlan_cp_stats_chipset_stats.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
3  * SPDX-License-Identifier: ISC
4  */
5 
6 /**
7  * DOC: This file contains chipset stats implementstion
8  */
9 
10 #ifdef WLAN_CHIPSET_STATS
11 #include <qdf_mem.h>
12 #include <qdf_types.h>
13 #include <qdf_status.h>
14 #include <qdf_trace.h>
15 #include <qdf_time.h>
16 #include <qdf_mc_timer.h>
17 #include <qdf_lock.h>
18 #include <qdf_str.h>
19 #include <qdf_module.h>
20 #include <wlan_nlink_common.h>
21 #include <wlan_cp_stats_chipset_stats.h>
22 #include "wlan_cp_stats_obj_mgr_handler.h"
23 
24 static struct chipset_stats cstats;
25 static struct cstats_node *gcstats_buffer[CSTATS_MAX_TYPE];
26 
wlan_cp_stats_cstats_init(struct wlan_objmgr_psoc * psoc)27 QDF_STATUS wlan_cp_stats_cstats_init(struct wlan_objmgr_psoc *psoc)
28 {
29 	qdf_list_node_t *tmp_node = NULL;
30 	int i, j, k;
31 
32 	if (!wlan_cp_stats_get_chipset_stats_enable(psoc)) {
33 		qdf_info("Chipset Stats feature is disabled");
34 		cstats.is_cstats_ini_enabled = false;
35 		return QDF_STATUS_SUCCESS;
36 	}
37 
38 	cstats.is_cstats_ini_enabled = true;
39 
40 	for (i = 0; i < CSTATS_MAX_TYPE; i++) {
41 		qdf_spinlock_create(&cstats.cstats_lock[i]);
42 
43 		gcstats_buffer[i] = qdf_mem_valloc(MAX_CSTATS_NODE_COUNT *
44 						   sizeof(struct cstats_node));
45 		if (!gcstats_buffer[i]) {
46 			qdf_err("Could not allocate memory for chipset stats");
47 			for (k = 0; k < i ; k++) {
48 				qdf_spin_lock_bh(&cstats.cstats_lock[k]);
49 				cstats.ccur_node[k] = NULL;
50 				qdf_spin_unlock_bh(&cstats.cstats_lock[k]);
51 
52 				if (gcstats_buffer[k])
53 					qdf_mem_vfree(gcstats_buffer[k]);
54 			}
55 
56 			return QDF_STATUS_E_NOMEM;
57 		}
58 
59 		qdf_mem_zero(gcstats_buffer[i], MAX_CSTATS_NODE_COUNT *
60 			     sizeof(struct cstats_node));
61 
62 		qdf_spin_lock_bh(&cstats.cstats_lock[i]);
63 		qdf_init_list_head(&cstats.cstat_free_list[i].anchor);
64 		qdf_init_list_head(&cstats.cstat_filled_list[i].anchor);
65 
66 		for (j = 0; j < MAX_CSTATS_NODE_COUNT; j++) {
67 			qdf_list_insert_front(&cstats.cstat_free_list[i],
68 					      &gcstats_buffer[i][j].node);
69 			gcstats_buffer[i][j].index = j;
70 		}
71 
72 		qdf_list_remove_front(&cstats.cstat_free_list[i], &tmp_node);
73 
74 		cstats.ccur_node[i] =
75 			qdf_container_of(tmp_node, struct cstats_node, node);
76 		cstats.cstats_no_flush[i] = true;
77 		qdf_spin_unlock_bh(&cstats.cstats_lock[i]);
78 	}
79 
80 	return 0;
81 }
82 
wlan_cp_stats_cstats_deinit(void)83 void wlan_cp_stats_cstats_deinit(void)
84 {
85 	int i;
86 
87 	if (!cstats.is_cstats_ini_enabled) {
88 		qdf_info("Chipset Stats feature is disabled");
89 		return;
90 	}
91 
92 	for (i = 0; i < CSTATS_MAX_TYPE; i++) {
93 		qdf_spin_lock_bh(&cstats.cstats_lock[i]);
94 		cstats.ccur_node[i] = NULL;
95 		cstats.cstat_drop_cnt[i] = 0;
96 		qdf_spin_unlock_bh(&cstats.cstats_lock[i]);
97 
98 		if (gcstats_buffer[i]) {
99 			qdf_mem_vfree(gcstats_buffer[i]);
100 			gcstats_buffer[i] = NULL;
101 		}
102 	}
103 }
104 
wlan_cp_stats_cstats_register_tx_rx_ops(struct cstats_tx_rx_ops * ops)105 void wlan_cp_stats_cstats_register_tx_rx_ops(struct cstats_tx_rx_ops *ops)
106 {
107 	cstats.ops.cstats_send_data_to_usr = ops->cstats_send_data_to_usr;
108 }
109 
110 /* Need to call this with spin_lock acquired */
wlan_cp_stats_get_cstats_free_node(enum cstats_types type)111 static void wlan_cp_stats_get_cstats_free_node(enum cstats_types type)
112 {
113 	qdf_list_node_t *tmp_node = NULL;
114 
115 	if (cstats.ccur_node[type]->filled_length) {
116 		qdf_list_insert_back(&cstats.cstat_filled_list[type],
117 				     &cstats.ccur_node[type]->node);
118 	} else {
119 		return;
120 	}
121 
122 	if (!qdf_list_empty(&cstats.cstat_free_list[type])) {
123 		qdf_list_remove_front(&cstats.cstat_free_list[type], &tmp_node);
124 		cstats.ccur_node[type] =
125 			qdf_container_of(tmp_node, struct cstats_node, node);
126 	} else if (!qdf_list_empty(&cstats.cstat_filled_list[type])) {
127 		qdf_list_remove_front(&cstats.cstat_filled_list[type],
128 				      &tmp_node);
129 		cstats.ccur_node[type] =
130 			qdf_container_of(tmp_node, struct cstats_node, node);
131 		qdf_err("Dropping a chipset stats node : count %d",
132 			++(cstats.cstat_drop_cnt[type]));
133 	}
134 
135 	/* Reset the current node values */
136 	cstats.ccur_node[type]->filled_length = 0;
137 }
138 
wlan_cp_stats_cstats_write_to_buff(enum cstats_types type,void * to_be_sent,uint32_t plen)139 void wlan_cp_stats_cstats_write_to_buff(enum cstats_types type,
140 					void *to_be_sent,
141 					uint32_t plen)
142 {
143 	char *ptr;
144 	unsigned int *pfilled_length;
145 	unsigned int tlen;
146 
147 	if (!cstats.is_cstats_ini_enabled)
148 		return;
149 
150 	/* tAniNlHdr + Start Marker + End Marker */
151 	tlen = sizeof(tAniNlHdr) + (2 * CSTATS_MARKER_SZ);
152 
153 	if ((tlen + plen) > MAX_CSTATS_NODE_LENGTH) {
154 		qdf_err("The Buffer is too long");
155 		return;
156 	}
157 
158 	if (!cstats.ccur_node[type]) {
159 		qdf_err("Current Node is NULL");
160 		return;
161 	}
162 
163 	qdf_spin_lock_bh(&cstats.cstats_lock[type]);
164 
165 	pfilled_length = &cstats.ccur_node[type]->filled_length;
166 
167 	/* Check if we can accommodate more log into current node/buffer */
168 	if ((MAX_CSTATS_NODE_LENGTH - *pfilled_length) < (tlen + plen)) {
169 		wlan_cp_stats_get_cstats_free_node(type);
170 		pfilled_length = &cstats.ccur_node[type]->filled_length;
171 	}
172 
173 	if (type == CSTATS_HOST_TYPE) {
174 		/* Marker will be added while flushing to userspace*/
175 		ptr = &cstats.ccur_node[type]->logbuf[sizeof(tAniHdr) +
176 						      CSTATS_MARKER_SZ];
177 		memcpy(&ptr[*pfilled_length], to_be_sent, plen);
178 		*pfilled_length += plen;
179 	} else if (type == CSTATS_FW_TYPE) {
180 		ptr = &cstats.ccur_node[type]->logbuf[sizeof(tAniHdr)];
181 		memcpy(&ptr[*pfilled_length], CSTATS_FW_START_MARKER,
182 		       CSTATS_MARKER_SZ);
183 		memcpy(&ptr[*pfilled_length + CSTATS_MARKER_SZ], to_be_sent,
184 		       plen);
185 		memcpy(&ptr[*pfilled_length + CSTATS_MARKER_SZ + plen],
186 		       CSTATS_FW_END_MARKER, CSTATS_MARKER_SZ);
187 		*pfilled_length += (plen + 2 * CSTATS_MARKER_SZ);
188 	}
189 
190 	qdf_spin_unlock_bh(&cstats.cstats_lock[type]);
191 }
192 
wlan_cp_stats_cstats_send_version_to_usr(void)193 static int wlan_cp_stats_cstats_send_version_to_usr(void)
194 {
195 	uint8_t buff[MAX_CSTATS_VERSION_BUFF_LENGTH] = {0};
196 	uint8_t n;
197 	int metadata_len;
198 	int ret = -1;
199 
200 	metadata_len = sizeof(tAniHdr) + (2 * CSTATS_MARKER_SZ);
201 
202 	memcpy(&buff[sizeof(tAniHdr)], CSTATS_HOST_START_MARKER,
203 	       CSTATS_MARKER_SZ);
204 
205 	n = scnprintf(&buff[sizeof(tAniHdr) + CSTATS_MARKER_SZ],
206 		      MAX_CSTATS_VERSION_BUFF_LENGTH - metadata_len,
207 		      "[%s : %d, %s : %d, %s : %d]",
208 		      "Chispet stats - hdr_version",
209 		      CHIPSET_STATS_HDR_VERSION, "Endianness",
210 		      CHIPSET_STATS_MACHINE_ENDIANNESS, "Drop cnt",
211 		      cstats.cstat_drop_cnt[CSTATS_HOST_TYPE]);
212 
213 	qdf_mem_copy(&buff[sizeof(tAniHdr) + CSTATS_MARKER_SZ + n],
214 		     CSTATS_HOST_END_MARKER, CSTATS_MARKER_SZ);
215 
216 	buff[metadata_len + n] = '\0';
217 
218 	if (cstats.ops.cstats_send_data_to_usr) {
219 		ret = cstats.ops.cstats_send_data_to_usr(buff,
220 							 metadata_len + n,
221 							 CSTATS_HOST_TYPE);
222 	}
223 
224 	if (ret)
225 		qdf_err("failed to send version info");
226 
227 	return ret;
228 }
229 
wlan_cp_stats_cstats_send_buffer_to_user(enum cstats_types type)230 int wlan_cp_stats_cstats_send_buffer_to_user(enum cstats_types type)
231 {
232 	int ret = -1;
233 	struct cstats_node *clog_msg;
234 	struct cstats_node *next;
235 	int payload_len;
236 	int mark_total;
237 	char *ptr = NULL;
238 
239 	if (!cstats.is_cstats_ini_enabled)
240 		return QDF_STATUS_SUCCESS;
241 
242 	qdf_spin_lock_bh(&cstats.cstats_lock[type]);
243 	wlan_cp_stats_get_cstats_free_node(type);
244 	qdf_spin_unlock_bh(&cstats.cstats_lock[type]);
245 
246 	if (type == CSTATS_HOST_TYPE) {
247 		ret = wlan_cp_stats_cstats_send_version_to_usr();
248 		if (ret)
249 			return ret;
250 	}
251 
252 	/*
253 	 * For fw stats the markers are already added at start and end of the
254 	 * each event
255 	 */
256 	if (type == CSTATS_HOST_TYPE)
257 		mark_total = (2 * CSTATS_MARKER_SZ);
258 	else if (type == CSTATS_FW_TYPE)
259 		mark_total = 0;
260 
261 	qdf_list_for_each_del(&cstats.cstat_filled_list[type],
262 			      clog_msg, next, node) {
263 		qdf_spin_lock_bh(&cstats.cstats_lock[type]);
264 
265 		/* For host stats marksers are added per node basis*/
266 		if (type == CSTATS_HOST_TYPE) {
267 			ptr = &clog_msg->logbuf[sizeof(tAniHdr)];
268 			qdf_mem_copy(ptr, CSTATS_HOST_START_MARKER,
269 				     CSTATS_MARKER_SZ);
270 			ptr = &clog_msg->logbuf[sizeof(tAniHdr) +
271 						CSTATS_MARKER_SZ +
272 						clog_msg->filled_length];
273 			qdf_mem_copy(ptr, CSTATS_HOST_END_MARKER,
274 				     CSTATS_MARKER_SZ);
275 		}
276 
277 		if (!cstats.cstats_no_flush[type]) {
278 			qdf_list_remove_node(&cstats.cstat_free_list[type],
279 					     &clog_msg->node);
280 		}
281 
282 		qdf_spin_unlock_bh(&cstats.cstats_lock[type]);
283 
284 		payload_len = clog_msg->filled_length + sizeof(tAniHdr) +
285 			      mark_total;
286 
287 		if (cstats.ops.cstats_send_data_to_usr) {
288 			ret = cstats.ops.cstats_send_data_to_usr
289 			       (clog_msg->logbuf, payload_len, type);
290 		}
291 
292 		if (ret) {
293 			qdf_err("Send Failed %d drop_count = %u", ret,
294 				++(cstats.cstat_drop_cnt[type]));
295 		}
296 
297 		if (!cstats.cstats_no_flush[type]) {
298 			qdf_spin_lock_bh(&cstats.cstats_lock[type]);
299 			qdf_list_insert_back(&cstats.cstat_free_list[type],
300 					     &clog_msg->node);
301 			qdf_spin_unlock_bh(&cstats.cstats_lock[type]);
302 		}
303 	}
304 
305 	return ret;
306 }
307 
308 static inline enum cstats_pkt_type
get_cstat_type(enum qdf_proto_type type,enum qdf_proto_subtype subtype)309 get_cstat_type(enum qdf_proto_type type,
310 	       enum qdf_proto_subtype subtype)
311 {
312 	if (type == QDF_PROTO_TYPE_EAPOL) {
313 		if (subtype == QDF_PROTO_EAPOL_M1)
314 			return CSTATS_EAPOL_M1;
315 		else if (subtype == QDF_PROTO_EAPOL_M2)
316 			return CSTATS_EAPOL_M2;
317 		else if (subtype == QDF_PROTO_EAPOL_M3)
318 			return CSTATS_EAPOL_M3;
319 		else if (subtype == QDF_PROTO_EAPOL_M4)
320 			return CSTATS_EAPOL_M4;
321 	} else if (type == QDF_PROTO_TYPE_DHCP) {
322 		if (subtype == QDF_PROTO_DHCP_DISCOVER)
323 			return CSTATS_DHCP_DISCOVER;
324 		else if (subtype == QDF_PROTO_DHCP_REQUEST)
325 			return CSTATS_DHCP_REQ;
326 		else if (subtype == QDF_PROTO_DHCP_OFFER)
327 			return CSTATS_DHCP_OFFER;
328 		else if (subtype == QDF_PROTO_DHCP_ACK)
329 			return CSTATS_DHCP_ACK;
330 		else if (subtype == QDF_PROTO_DHCP_NACK)
331 			return CSTATS_DHCP_NACK;
332 		else if (subtype == QDF_PROTO_DHCP_RELEASE)
333 			return CSTATS_DHCP_RELEASE;
334 		else if (subtype == QDF_PROTO_DHCP_DECLINE)
335 			return CSTATS_DHCP_DECLINE;
336 		else if (subtype == QDF_PROTO_DHCP_INFORM)
337 			return CSTATS_DHCP_INFORM;
338 	}
339 
340 	return CSTATS_PKT_TYPE_INVALID;
341 }
342 
343 static inline enum cstats_dir
get_cstat_dir(enum qdf_proto_dir dir)344 get_cstat_dir(enum qdf_proto_dir dir)
345 {
346 	switch (dir) {
347 	case QDF_TX:
348 		return CSTATS_DIR_TX;
349 	case QDF_RX:
350 		return CSTATS_DIR_RX;
351 	default:
352 		return CSTATS_DIR_INVAL;
353 	}
354 }
355 
356 static inline enum cstats_pkt_status
get_cstat_pkt_status(enum qdf_dp_tx_rx_status status)357 get_cstat_pkt_status(enum qdf_dp_tx_rx_status status)
358 {
359 	switch (status) {
360 	case QDF_TX_RX_STATUS_INVALID:
361 		return CSTATS_STATUS_INVALID;
362 	case QDF_TX_RX_STATUS_OK:
363 		return CSTATS_TX_STATUS_OK;
364 	case QDF_TX_RX_STATUS_FW_DISCARD:
365 		return CSTATS_TX_STATUS_FW_DISCARD;
366 	case QDF_TX_RX_STATUS_NO_ACK:
367 		return CSTATS_TX_STATUS_NO_ACK;
368 	case QDF_TX_RX_STATUS_DROP:
369 		return CSTATS_TX_STATUS_DROP;
370 	case QDF_TX_RX_STATUS_DOWNLOAD_SUCC:
371 		return CSTATS_TX_STATUS_DOWNLOAD_SUCC;
372 	case QDF_TX_RX_STATUS_DEFAULT:
373 		return CSTATS_TX_STATUS_DEFAULT;
374 	default:
375 		return CSTATS_STATUS_INVALID;
376 	}
377 }
378 
wlan_cp_stats_cstats_pkt_log(uint8_t * sa,uint8_t * da,enum qdf_proto_type pkt_type,enum qdf_proto_subtype subtype,enum qdf_proto_dir dir,enum qdf_dp_tx_rx_status status,uint8_t vdev_id,enum QDF_OPMODE op_mode)379 void wlan_cp_stats_cstats_pkt_log(uint8_t *sa, uint8_t *da,
380 				  enum qdf_proto_type pkt_type,
381 				  enum qdf_proto_subtype subtype,
382 				  enum qdf_proto_dir dir,
383 				  enum qdf_dp_tx_rx_status status,
384 				  uint8_t vdev_id,
385 				  enum QDF_OPMODE op_mode)
386 {
387 	struct cstats_pkt_info stat = {0};
388 
389 	stat.cmn.hdr.evt_id = WLAN_CHIPSET_STATS_DATA_PKT_EVENT_ID;
390 	stat.cmn.hdr.length = sizeof(struct cstats_pkt_info) -
391 				sizeof(struct cstats_hdr);
392 	stat.cmn.opmode = op_mode;
393 	stat.cmn.vdev_id = vdev_id;
394 	stat.cmn.timestamp_us = qdf_get_time_of_the_day_us();
395 	stat.cmn.time_tick = qdf_get_log_timestamp();
396 
397 	CSTATS_MAC_COPY(stat.src_mac, sa);
398 	CSTATS_MAC_COPY(stat.dst_mac, da);
399 
400 	stat.type = get_cstat_type(pkt_type, subtype);
401 	stat.dir = get_cstat_dir(dir);
402 	stat.status = get_cstat_pkt_status(status);
403 
404 	wlan_cstats_host_stats(sizeof(struct cstats_pkt_info), &stat);
405 }
406 #endif /* WLAN_CHIPSET_STATS */
407