xref: /wlan-dirver/qca-wifi-host-cmn/umac/cmn_services/obj_mgr/src/wlan_objmgr_debug.c (revision 11f5a63a6cbdda84849a730de22f0a71e635d58c)
1 /*
2  *
3  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 /*
20  * DOC: Public APIs to perform debug operations on object manager
21  */
22 
23 #include <wlan_objmgr_psoc_obj.h>
24 #include <wlan_objmgr_pdev_obj.h>
25 #include <wlan_objmgr_vdev_obj.h>
26 #include <wlan_objmgr_peer_obj.h>
27 #include "wlan_objmgr_global_obj_i.h"
28 #include <qdf_mem.h>
29 #include <qdf_platform.h>
30 
31 /*
32  * Default TTL (of FW) for mgmt frames is 5 sec, by considering all the other
33  * delays, arrived with this value
34  */
35 #define LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC   8000
36 #define LOG_DEL_OBJ_DESTROY_DURATION_SEC 8
37 /*
38  * The max duration for which a obj can be allowed to remain in L-state
39  * The duration  should be higher than the psoc idle timeout.
40  */
41 #define LOG_DEL_OBJ_DESTROY_ASSERT_DURATION_SEC 24
42 #define LOG_DEL_OBJ_LIST_MAX_COUNT       (3 + 5 + 48 + 4096)
43 
44 union wlan_objmgr_del_obj {
45 	struct wlan_objmgr_psoc *obj_psoc;
46 	struct wlan_objmgr_pdev *obj_pdev;
47 	struct wlan_objmgr_vdev *obj_vdev;
48 	struct wlan_objmgr_peer *obj_peer;
49 };
50 
51 /**
52  * struct log_del_obj    - Logically deleted Object
53  * @obj:            Represents peer/vdev/pdev/psoc
54  * @node:           List node from Logically deleted list
55  * @obj_type:       Object type for peer/vdev/pdev/psoc
56  * @tstamp:         Timestamp when node entered logically
57  *                  deleted state
58  */
59 struct log_del_obj {
60 	union wlan_objmgr_del_obj obj;
61 	qdf_list_node_t node;
62 	enum wlan_objmgr_obj_type obj_type;
63 	qdf_time_t tstamp;
64 };
65 
66 /**
67  * struct wlan_objmgr_debug_info     - Objmgr debug info
68  * for Logically deleted object
69  * @obj_timer:          Timer object
70  * @obj_list:           list object having linking logically
71  *                       deleted nodes
72  * @list_lock:          lock to protect list
73  */
74 struct wlan_objmgr_debug_info {
75 	qdf_timer_t obj_timer;
76 	qdf_list_t obj_list;
77 	qdf_spinlock_t list_lock;
78 };
79 
80 static const char *
81 wlan_obj_type_get_obj_name(enum wlan_objmgr_obj_type obj_type)
82 {
83 	static const struct wlan_obj_type_to_name {
84 		enum wlan_objmgr_obj_type obj_type;
85 		const char *name;
86 	} obj_type_name[WLAN_OBJ_TYPE_MAX] = {
87 		{WLAN_PSOC_OP, "psoc"},
88 		{WLAN_PDEV_OP, "pdev"},
89 		{WLAN_VDEV_OP, "vdev"},
90 		{WLAN_PEER_OP, "peer"}
91 	};
92 	uint8_t idx;
93 
94 	for (idx = 0; idx < WLAN_OBJ_TYPE_MAX; idx++) {
95 		if (obj_type == obj_type_name[idx].obj_type)
96 			return obj_type_name[idx].name;
97 	}
98 
99 	return NULL;
100 }
101 
102 static uint8_t*
103 wlan_objmgr_debug_get_macaddr(union wlan_objmgr_del_obj *obj,
104 			      enum wlan_objmgr_obj_type obj_type)
105 {
106 	switch (obj_type) {
107 	case WLAN_PSOC_OP:
108 		return wlan_psoc_get_hw_macaddr(obj->obj_psoc);
109 	case WLAN_PDEV_OP:
110 		return wlan_pdev_get_hw_macaddr(obj->obj_pdev);
111 	case WLAN_VDEV_OP:
112 		return wlan_vdev_mlme_get_macaddr(obj->obj_vdev);
113 	case WLAN_PEER_OP:
114 		return wlan_peer_get_macaddr(obj->obj_peer);
115 	default:
116 		obj_mgr_err("invalid obj_type");
117 		return NULL;
118 	}
119 }
120 
121 static void
122 wlan_objmgr_insert_ld_obj_to_list(struct wlan_objmgr_debug_info *debug_info,
123 				  qdf_list_node_t *node)
124 {
125 	/* Insert object to list with lock being held*/
126 	qdf_spin_lock_bh(&debug_info->list_lock);
127 
128 	/* Start timer only when list is empty */
129 	if (qdf_list_empty(&debug_info->obj_list))
130 		qdf_timer_start(&debug_info->obj_timer,
131 				LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC);
132 
133 	qdf_list_insert_back(&debug_info->obj_list, node);
134 	qdf_spin_unlock_bh(&debug_info->list_lock);
135 }
136 
137 static void wlan_obj_type_get_obj(union wlan_objmgr_del_obj *obj,
138 				  union wlan_objmgr_del_obj *del_obj,
139 				  enum wlan_objmgr_obj_type obj_type)
140 {
141 	switch (obj_type) {
142 	case WLAN_PSOC_OP:
143 		del_obj->obj_psoc = obj->obj_psoc;
144 		return;
145 	case WLAN_PDEV_OP:
146 		del_obj->obj_pdev = obj->obj_pdev;
147 		return;
148 	case WLAN_VDEV_OP:
149 		del_obj->obj_vdev = obj->obj_vdev;
150 		return;
151 	case WLAN_PEER_OP:
152 		del_obj->obj_peer = obj->obj_peer;
153 		return;
154 	default:
155 		obj_mgr_err("invalid obj_type");
156 		return;
157 	}
158 }
159 
160 void wlan_objmgr_notify_log_delete(void *obj,
161 				   enum wlan_objmgr_obj_type obj_type)
162 {
163 	struct wlan_objmgr_debug_info *debug_info;
164 	const char *obj_name;
165 	uint8_t *macaddr;
166 	qdf_time_t tstamp;
167 	struct log_del_obj *node;
168 	union wlan_objmgr_del_obj *del_obj = (union wlan_objmgr_del_obj *)&obj;
169 
170 	if (!obj) {
171 		obj_mgr_err("object is null");
172 		return;
173 	}
174 
175 	qdf_spin_lock_bh(&g_umac_glb_obj->global_lock);
176 	debug_info = g_umac_glb_obj->debug_info;
177 	qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock);
178 
179 	if (!debug_info) {
180 		obj_mgr_err("debug_info is null");
181 		return;
182 	}
183 
184 	macaddr = wlan_objmgr_debug_get_macaddr(del_obj, obj_type);
185 	if (!macaddr) {
186 		obj_mgr_err("macaddr is null");
187 		return;
188 	}
189 
190 	obj_name = wlan_obj_type_get_obj_name(obj_type);
191 	if (!obj_name) {
192 		obj_mgr_err("obj_name is null");
193 		return;
194 	}
195 
196 	tstamp = qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000;
197 	node = qdf_mem_malloc(sizeof(*node));
198 	if (!node)
199 		return;
200 
201 	wlan_obj_type_get_obj(del_obj, &node->obj, obj_type);
202 	node->obj_type = obj_type;
203 	node->tstamp = tstamp;
204 	obj_mgr_debug("#%s : mac_addr :" QDF_MAC_ADDR_STR" entered L-state",
205 		      obj_name, QDF_MAC_ADDR_ARRAY(macaddr));
206 	wlan_objmgr_insert_ld_obj_to_list(debug_info, &node->node);
207 }
208 
209 static bool wlan_objmgr_del_obj_match(union wlan_objmgr_del_obj *obj,
210 				      union wlan_objmgr_del_obj *del_obj,
211 				      enum wlan_objmgr_obj_type obj_type)
212 {
213 	switch (obj_type) {
214 	case WLAN_PSOC_OP:
215 		if (del_obj->obj_psoc == obj->obj_psoc)
216 			return true;
217 	case WLAN_PDEV_OP:
218 		if (del_obj->obj_pdev == obj->obj_pdev)
219 			return true;
220 	case WLAN_VDEV_OP:
221 		if (del_obj->obj_vdev == obj->obj_vdev)
222 			return true;
223 	case WLAN_PEER_OP:
224 		if (del_obj->obj_peer == obj->obj_peer)
225 			return true;
226 	default:
227 		return false;
228 	}
229 }
230 
231 static void
232 wlan_objmgr_rem_ld_obj_from_list(union wlan_objmgr_del_obj *obj,
233 				 struct wlan_objmgr_debug_info *debug_info,
234 				 enum wlan_objmgr_obj_type obj_type)
235 {
236 	qdf_list_node_t *node = NULL;
237 	struct log_del_obj *obj_to_remove = NULL;
238 	qdf_list_t *list;
239 	QDF_STATUS status;
240 
241 	list = &debug_info->obj_list;
242 	qdf_spin_lock_bh(&debug_info->list_lock);
243 	status = qdf_list_peek_front(list, &node);
244 
245 	while (QDF_IS_STATUS_SUCCESS(status)) {
246 		obj_to_remove = qdf_container_of(node,
247 						 struct log_del_obj, node);
248 		if (wlan_objmgr_del_obj_match(obj, &obj_to_remove->obj,
249 					      obj_type) &&
250 		    obj_to_remove->obj_type == obj_type) {
251 			status = qdf_list_remove_node(list,
252 						      &obj_to_remove->node);
253 			/* Stop timer if list is empty */
254 			if (QDF_IS_STATUS_SUCCESS(status)) {
255 				if (qdf_list_empty(&debug_info->obj_list))
256 					qdf_timer_stop(&debug_info->obj_timer);
257 				qdf_mem_free(obj_to_remove);
258 			}
259 			break;
260 		}
261 		status = qdf_list_peek_next(list, node, &node);
262 	};
263 	qdf_spin_unlock_bh(&debug_info->list_lock);
264 }
265 
266 void wlan_objmgr_notify_destroy(void *obj,
267 				enum wlan_objmgr_obj_type obj_type)
268 {
269 	struct wlan_objmgr_debug_info *debug_info;
270 	uint8_t *macaddr;
271 	const char *obj_name;
272 	union wlan_objmgr_del_obj *del_obj = (union wlan_objmgr_del_obj *)&obj;
273 
274 	qdf_spin_lock_bh(&g_umac_glb_obj->global_lock);
275 	debug_info = g_umac_glb_obj->debug_info;
276 	qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock);
277 
278 	if (!debug_info) {
279 		obj_mgr_err("debug_info is null");
280 		return;
281 	}
282 	macaddr = wlan_objmgr_debug_get_macaddr(del_obj, obj_type);
283 	if (!macaddr) {
284 		obj_mgr_err("macaddr is null");
285 		return;
286 	}
287 	obj_name = wlan_obj_type_get_obj_name(obj_type);
288 	if (!obj_name) {
289 		obj_mgr_err("obj_name is null");
290 		return;
291 	}
292 	obj_mgr_debug("#%s, macaddr: " QDF_MAC_ADDR_STR" exited L-state",
293 		      obj_name, QDF_MAC_ADDR_ARRAY(macaddr));
294 
295 	wlan_objmgr_rem_ld_obj_from_list(del_obj,
296 					 debug_info, obj_type);
297 }
298 
299 /**
300  * wlan_objmgr_debug_obj_destroyed_panic() - Panic in case obj is in L-state
301  * for long
302  * @obj_name: The name of the module ID
303  *
304  * This will invoke panic in the case that the obj is in logically destroyed
305  * state for a long time. The panic is invoked only in case feature flag
306  * WLAN_OBJMGR_PANIC_ON_BUG is enabled
307  *
308  * Return: None
309  */
310 #ifdef CONFIG_LEAK_DETECTION
311 static inline void wlan_objmgr_debug_obj_destroyed_panic(const char *obj_name)
312 {
313 	obj_mgr_alert("#%s in L-state for too long!", obj_name);
314 	QDF_BUG(0);
315 }
316 #else
317 static inline void wlan_objmgr_debug_obj_destroyed_panic(const char *obj_name)
318 {
319 }
320 #endif
321 
322 /*
323  * wlan_objmgr_print_pending_refs() - Print pending refs according to the obj
324  * @obj:	Represents peer/vdev/pdev/psoc
325  * @obj_type:	Object type for peer/vdev/pdev/psoc
326  *
327  * Return: None
328  */
329 static void wlan_objmgr_print_pending_refs(union wlan_objmgr_del_obj *obj,
330 					   enum wlan_objmgr_obj_type obj_type)
331 {
332 	switch (obj_type) {
333 	case WLAN_PSOC_OP:
334 		wlan_objmgr_print_ref_ids(obj->obj_psoc->soc_objmgr.ref_id_dbg,
335 					  QDF_TRACE_LEVEL_DEBUG);
336 		break;
337 	case WLAN_PDEV_OP:
338 		wlan_objmgr_print_ref_ids(obj->obj_pdev->pdev_objmgr.ref_id_dbg,
339 					  QDF_TRACE_LEVEL_DEBUG);
340 		break;
341 	case WLAN_VDEV_OP:
342 		wlan_objmgr_print_ref_ids(obj->obj_vdev->vdev_objmgr.ref_id_dbg,
343 					  QDF_TRACE_LEVEL_DEBUG);
344 		break;
345 	case WLAN_PEER_OP:
346 		wlan_objmgr_print_ref_ids(obj->obj_peer->peer_objmgr.ref_id_dbg,
347 					  QDF_TRACE_LEVEL_DEBUG);
348 		break;
349 	default:
350 		obj_mgr_debug("invalid obj_type");
351 	}
352 }
353 
354 /* timeout handler for iterating logically deleted object */
355 
356 static void wlan_objmgr_iterate_log_del_obj_handler(void *timer_arg)
357 {
358 	enum wlan_objmgr_obj_type obj_type;
359 	uint8_t *macaddr;
360 	const char *obj_name;
361 	struct wlan_objmgr_debug_info *debug_info;
362 	qdf_list_node_t *node;
363 	qdf_list_t *log_del_obj_list = NULL;
364 	struct log_del_obj *del_obj = NULL;
365 	qdf_time_t cur_tstamp;
366 	QDF_STATUS status;
367 
368 	qdf_spin_lock_bh(&g_umac_glb_obj->global_lock);
369 	debug_info = g_umac_glb_obj->debug_info;
370 	qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock);
371 
372 	if (!debug_info) {
373 		obj_mgr_err("debug_info is not initialized");
374 		return;
375 	}
376 
377 	log_del_obj_list = &debug_info->obj_list;
378 	qdf_spin_lock_bh(&debug_info->list_lock);
379 
380 	status = qdf_list_peek_front(log_del_obj_list, &node);
381 	if (QDF_IS_STATUS_ERROR(status)) {
382 		qdf_spin_unlock_bh(&debug_info->list_lock);
383 		return;
384 	}
385 
386 	/* compute the current timestamp in seconds
387 	 * need to compare with destroy duration of object
388 	 */
389 	cur_tstamp = (qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000);
390 
391 	do {
392 		del_obj = qdf_container_of(node, struct log_del_obj, node);
393 		obj_type = del_obj->obj_type;
394 		macaddr = wlan_objmgr_debug_get_macaddr(&del_obj->obj,
395 							obj_type);
396 		obj_name = wlan_obj_type_get_obj_name(obj_type);
397 
398 		/* If object is in logically deleted state for time more than
399 		 * destroy duration, print the object type and MAC
400 		 */
401 		if (cur_tstamp  < (del_obj->tstamp +
402 					LOG_DEL_OBJ_DESTROY_DURATION_SEC)) {
403 			break;
404 		}
405 		if (!macaddr) {
406 			obj_mgr_err("macaddr is null");
407 			QDF_BUG(0);
408 			break;
409 		}
410 		if (!obj_name) {
411 			obj_mgr_err("obj_name is null");
412 			QDF_BUG(0);
413 			break;
414 		}
415 
416 		obj_mgr_alert("#%s in L-state,MAC: " QDF_MAC_ADDR_STR,
417 			      obj_name, QDF_MAC_ADDR_ARRAY(macaddr));
418 		wlan_objmgr_print_pending_refs(&del_obj->obj, obj_type);
419 
420 		if (cur_tstamp > del_obj->tstamp +
421 		    LOG_DEL_OBJ_DESTROY_ASSERT_DURATION_SEC) {
422 			if (!qdf_is_recovering() && !qdf_is_fw_down())
423 				wlan_objmgr_debug_obj_destroyed_panic(obj_name);
424 		}
425 
426 		status = qdf_list_peek_next(log_del_obj_list, node, &node);
427 
428 	} while (QDF_IS_STATUS_SUCCESS(status));
429 
430 	qdf_timer_mod(&debug_info->obj_timer, LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC);
431 	qdf_spin_unlock_bh(&debug_info->list_lock);
432 }
433 
434 void wlan_objmgr_debug_info_deinit(void)
435 {
436 	struct log_del_obj *obj_to_remove;
437 	struct wlan_objmgr_debug_info *debug_info;
438 	qdf_list_node_t *node = NULL;
439 	qdf_list_t *list;
440 	bool is_child_alive = false;
441 
442 	qdf_spin_lock_bh(&g_umac_glb_obj->global_lock);
443 	debug_info = g_umac_glb_obj->debug_info;
444 	qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock);
445 
446 	if (!debug_info) {
447 		obj_mgr_err("debug_info is not initialized");
448 		return;
449 	}
450 	list = &debug_info->obj_list;
451 
452 	qdf_spin_lock_bh(&debug_info->list_lock);
453 
454 	/* Check if any child of global object is in L-state and remove it,
455 	 * ideally it shouldn't be
456 	 */
457 	while (qdf_list_remove_front(list, &node) == QDF_STATUS_SUCCESS) {
458 		is_child_alive = true;
459 		obj_to_remove = qdf_container_of(node,
460 						 struct log_del_obj, node);
461 		if (qdf_list_empty(&debug_info->obj_list))
462 			qdf_timer_stop(&debug_info->obj_timer);
463 		/* free the object */
464 		qdf_mem_free(obj_to_remove);
465 	}
466 	qdf_spin_unlock_bh(&debug_info->list_lock);
467 
468 	if (is_child_alive) {
469 		obj_mgr_alert("This shouldn't happen!!, No child of global"
470 			       "object should be in L-state, as global obj"
471 				"is going to destroy");
472 		QDF_BUG(0);
473 	}
474 
475 	/* free timer, destroy spinlock, list and debug_info object as
476 	 * global object is going to free
477 	 */
478 	qdf_list_destroy(list);
479 	qdf_timer_free(&debug_info->obj_timer);
480 	qdf_spinlock_destroy(&debug_info->list_lock);
481 	qdf_mem_free(debug_info);
482 
483 	qdf_spin_lock_bh(&g_umac_glb_obj->global_lock);
484 	g_umac_glb_obj->debug_info = NULL;
485 	qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock);
486 }
487 
488 void wlan_objmgr_debug_info_init(void)
489 {
490 	struct wlan_objmgr_debug_info *debug_info;
491 
492 	debug_info = qdf_mem_malloc(sizeof(*debug_info));
493 	if (!debug_info) {
494 		g_umac_glb_obj->debug_info = NULL;
495 		return;
496 	}
497 
498 	/* Initialize timer with timeout handler */
499 	qdf_timer_init(NULL, &debug_info->obj_timer,
500 		       wlan_objmgr_iterate_log_del_obj_handler,
501 		       NULL, QDF_TIMER_TYPE_WAKE_APPS);
502 
503 	/* Initialze the node_count to 0 and create list*/
504 	qdf_list_create(&debug_info->obj_list,
505 			LOG_DEL_OBJ_LIST_MAX_COUNT);
506 
507 	/* Initialize the spin_lock to protect list */
508 	qdf_spinlock_create(&debug_info->list_lock);
509 
510 	/* attach debug_info object to global object */
511 	qdf_spin_lock_bh(&g_umac_glb_obj->global_lock);
512 	g_umac_glb_obj->debug_info = debug_info;
513 	qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock);
514 }
515