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