1 /* 2 * 3 * Copyright (c) 2018-2020 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_debug.h> 28 #include "wlan_objmgr_global_obj_i.h" 29 #include <qdf_mem.h> 30 #include <qdf_platform.h> 31 #include <qdf_str.h> 32 33 /* 34 * Default TTL (of FW) for mgmt frames is 5 sec, by considering all the other 35 * delays, arrived with this value 36 */ 37 #define LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC 8000 38 #define LOG_DEL_OBJ_DESTROY_DURATION_SEC 8 39 /* 40 * The max duration for which a obj can be allowed to remain in L-state 41 * The duration should be higher than the psoc idle timeout. 42 */ 43 #define LOG_DEL_OBJ_DESTROY_ASSERT_DURATION_SEC 32 44 #define LOG_DEL_OBJ_LIST_MAX_COUNT (3 + 5 + 48 + 4096) 45 46 union wlan_objmgr_del_obj { 47 struct wlan_objmgr_psoc *obj_psoc; 48 struct wlan_objmgr_pdev *obj_pdev; 49 struct wlan_objmgr_vdev *obj_vdev; 50 struct wlan_objmgr_peer *obj_peer; 51 }; 52 53 /** 54 * struct log_del_obj - Logically deleted Object 55 * @obj: Represents peer/vdev/pdev/psoc 56 * @node: List node from Logically deleted list 57 * @obj_type: Object type for peer/vdev/pdev/psoc 58 * @tstamp: Timestamp when node entered logically 59 * deleted state 60 */ 61 struct log_del_obj { 62 union wlan_objmgr_del_obj obj; 63 qdf_list_node_t node; 64 enum wlan_objmgr_obj_type obj_type; 65 qdf_time_t tstamp; 66 }; 67 68 /** 69 * struct wlan_objmgr_debug_info - Objmgr debug info 70 * for Logically deleted object 71 * @obj_timer: Timer object 72 * @obj_list: list object having linking logically 73 * deleted nodes 74 * @list_lock: lock to protect list 75 */ 76 struct wlan_objmgr_debug_info { 77 qdf_timer_t obj_timer; 78 qdf_list_t obj_list; 79 qdf_spinlock_t list_lock; 80 }; 81 82 static const char * 83 wlan_obj_type_get_obj_name(enum wlan_objmgr_obj_type obj_type) 84 { 85 static const struct wlan_obj_type_to_name { 86 enum wlan_objmgr_obj_type obj_type; 87 const char *name; 88 } obj_type_name[WLAN_OBJ_TYPE_MAX] = { 89 {WLAN_PSOC_OP, "psoc"}, 90 {WLAN_PDEV_OP, "pdev"}, 91 {WLAN_VDEV_OP, "vdev"}, 92 {WLAN_PEER_OP, "peer"} 93 }; 94 uint8_t idx; 95 96 for (idx = 0; idx < WLAN_OBJ_TYPE_MAX; idx++) { 97 if (obj_type == obj_type_name[idx].obj_type) 98 return obj_type_name[idx].name; 99 } 100 101 return NULL; 102 } 103 104 static uint8_t* 105 wlan_objmgr_debug_get_macaddr(union wlan_objmgr_del_obj *obj, 106 enum wlan_objmgr_obj_type obj_type) 107 { 108 switch (obj_type) { 109 case WLAN_PSOC_OP: 110 return wlan_psoc_get_hw_macaddr(obj->obj_psoc); 111 case WLAN_PDEV_OP: 112 return wlan_pdev_get_hw_macaddr(obj->obj_pdev); 113 case WLAN_VDEV_OP: 114 return wlan_vdev_mlme_get_macaddr(obj->obj_vdev); 115 case WLAN_PEER_OP: 116 return wlan_peer_get_macaddr(obj->obj_peer); 117 default: 118 obj_mgr_err("invalid obj_type"); 119 return NULL; 120 } 121 } 122 123 static void 124 wlan_objmgr_insert_ld_obj_to_list(struct wlan_objmgr_debug_info *debug_info, 125 qdf_list_node_t *node) 126 { 127 /* Insert object to list with lock being held*/ 128 qdf_spin_lock_bh(&debug_info->list_lock); 129 130 /* Start timer only when list is empty */ 131 if (qdf_list_empty(&debug_info->obj_list)) 132 qdf_timer_start(&debug_info->obj_timer, 133 LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC); 134 135 qdf_list_insert_back(&debug_info->obj_list, node); 136 qdf_spin_unlock_bh(&debug_info->list_lock); 137 } 138 139 static void wlan_obj_type_get_obj(union wlan_objmgr_del_obj *obj, 140 union wlan_objmgr_del_obj *del_obj, 141 enum wlan_objmgr_obj_type obj_type) 142 { 143 switch (obj_type) { 144 case WLAN_PSOC_OP: 145 del_obj->obj_psoc = obj->obj_psoc; 146 return; 147 case WLAN_PDEV_OP: 148 del_obj->obj_pdev = obj->obj_pdev; 149 return; 150 case WLAN_VDEV_OP: 151 del_obj->obj_vdev = obj->obj_vdev; 152 return; 153 case WLAN_PEER_OP: 154 del_obj->obj_peer = obj->obj_peer; 155 return; 156 default: 157 obj_mgr_err("invalid obj_type"); 158 return; 159 } 160 } 161 162 void wlan_objmgr_notify_log_delete(void *obj, 163 enum wlan_objmgr_obj_type obj_type) 164 { 165 struct wlan_objmgr_debug_info *debug_info; 166 const char *obj_name; 167 uint8_t *macaddr; 168 qdf_time_t tstamp; 169 struct log_del_obj *node; 170 union wlan_objmgr_del_obj *del_obj = (union wlan_objmgr_del_obj *)&obj; 171 172 if (!obj) { 173 obj_mgr_err("object is null"); 174 return; 175 } 176 177 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 178 debug_info = g_umac_glb_obj->debug_info; 179 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 180 181 if (!debug_info) { 182 obj_mgr_err("debug_info is null"); 183 return; 184 } 185 186 macaddr = wlan_objmgr_debug_get_macaddr(del_obj, obj_type); 187 if (!macaddr) { 188 obj_mgr_err("macaddr is null"); 189 return; 190 } 191 192 obj_name = wlan_obj_type_get_obj_name(obj_type); 193 if (!obj_name) { 194 obj_mgr_err("obj_name is null"); 195 return; 196 } 197 198 tstamp = qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000; 199 node = qdf_mem_malloc(sizeof(*node)); 200 if (!node) 201 return; 202 203 wlan_obj_type_get_obj(del_obj, &node->obj, obj_type); 204 node->obj_type = obj_type; 205 node->tstamp = tstamp; 206 obj_mgr_debug("#%s : mac_addr :" QDF_MAC_ADDR_STR" entered L-state", 207 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 208 wlan_objmgr_insert_ld_obj_to_list(debug_info, &node->node); 209 } 210 211 static bool wlan_objmgr_del_obj_match(union wlan_objmgr_del_obj *obj, 212 union wlan_objmgr_del_obj *del_obj, 213 enum wlan_objmgr_obj_type obj_type) 214 { 215 switch (obj_type) { 216 case WLAN_PSOC_OP: 217 if (del_obj->obj_psoc == obj->obj_psoc) 218 return true; 219 case WLAN_PDEV_OP: 220 if (del_obj->obj_pdev == obj->obj_pdev) 221 return true; 222 case WLAN_VDEV_OP: 223 if (del_obj->obj_vdev == obj->obj_vdev) 224 return true; 225 case WLAN_PEER_OP: 226 if (del_obj->obj_peer == obj->obj_peer) 227 return true; 228 default: 229 return false; 230 } 231 } 232 233 static void 234 wlan_objmgr_rem_ld_obj_from_list(union wlan_objmgr_del_obj *obj, 235 struct wlan_objmgr_debug_info *debug_info, 236 enum wlan_objmgr_obj_type obj_type) 237 { 238 qdf_list_node_t *node = NULL; 239 struct log_del_obj *obj_to_remove = NULL; 240 qdf_list_t *list; 241 QDF_STATUS status; 242 243 list = &debug_info->obj_list; 244 qdf_spin_lock_bh(&debug_info->list_lock); 245 status = qdf_list_peek_front(list, &node); 246 247 while (QDF_IS_STATUS_SUCCESS(status)) { 248 obj_to_remove = qdf_container_of(node, 249 struct log_del_obj, node); 250 if (wlan_objmgr_del_obj_match(obj, &obj_to_remove->obj, 251 obj_type) && 252 obj_to_remove->obj_type == obj_type) { 253 status = qdf_list_remove_node(list, 254 &obj_to_remove->node); 255 /* Stop timer if list is empty */ 256 if (QDF_IS_STATUS_SUCCESS(status)) { 257 if (qdf_list_empty(&debug_info->obj_list)) 258 qdf_timer_stop(&debug_info->obj_timer); 259 qdf_mem_free(obj_to_remove); 260 } 261 break; 262 } 263 status = qdf_list_peek_next(list, node, &node); 264 }; 265 qdf_spin_unlock_bh(&debug_info->list_lock); 266 } 267 268 void wlan_objmgr_notify_destroy(void *obj, 269 enum wlan_objmgr_obj_type obj_type) 270 { 271 struct wlan_objmgr_debug_info *debug_info; 272 uint8_t *macaddr; 273 const char *obj_name; 274 union wlan_objmgr_del_obj *del_obj = (union wlan_objmgr_del_obj *)&obj; 275 276 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 277 debug_info = g_umac_glb_obj->debug_info; 278 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 279 280 if (!debug_info) { 281 obj_mgr_err("debug_info is null"); 282 return; 283 } 284 macaddr = wlan_objmgr_debug_get_macaddr(del_obj, obj_type); 285 if (!macaddr) { 286 obj_mgr_err("macaddr is null"); 287 return; 288 } 289 obj_name = wlan_obj_type_get_obj_name(obj_type); 290 if (!obj_name) { 291 obj_mgr_err("obj_name is null"); 292 return; 293 } 294 obj_mgr_debug("#%s, macaddr: " QDF_MAC_ADDR_STR" exited L-state", 295 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 296 297 wlan_objmgr_rem_ld_obj_from_list(del_obj, 298 debug_info, obj_type); 299 } 300 301 /** 302 * wlan_objmgr_debug_obj_destroyed_panic() - Panic in case obj is in L-state 303 * for long 304 * @obj_name: The name of the module ID 305 * 306 * This will invoke panic in the case that the obj is in logically destroyed 307 * state for a long time. The panic is invoked only in case feature flag 308 * WLAN_OBJMGR_PANIC_ON_BUG is enabled 309 * 310 * Return: None 311 */ 312 #ifdef CONFIG_LEAK_DETECTION 313 static inline void wlan_objmgr_debug_obj_destroyed_panic(const char *obj_name) 314 { 315 obj_mgr_alert("#%s in L-state for too long!", obj_name); 316 QDF_BUG(0); 317 } 318 #else 319 static inline void wlan_objmgr_debug_obj_destroyed_panic(const char *obj_name) 320 { 321 } 322 #endif 323 324 /* 325 * wlan_objmgr_print_pending_refs() - Print pending refs according to the obj 326 * @obj: Represents peer/vdev/pdev/psoc 327 * @obj_type: Object type for peer/vdev/pdev/psoc 328 * 329 * Return: None 330 */ 331 static void wlan_objmgr_print_pending_refs(union wlan_objmgr_del_obj *obj, 332 enum wlan_objmgr_obj_type obj_type) 333 { 334 switch (obj_type) { 335 case WLAN_PSOC_OP: 336 wlan_objmgr_print_ref_ids(obj->obj_psoc->soc_objmgr.ref_id_dbg, 337 QDF_TRACE_LEVEL_DEBUG); 338 break; 339 case WLAN_PDEV_OP: 340 wlan_objmgr_print_ref_ids(obj->obj_pdev->pdev_objmgr.ref_id_dbg, 341 QDF_TRACE_LEVEL_DEBUG); 342 break; 343 case WLAN_VDEV_OP: 344 wlan_objmgr_print_ref_ids(obj->obj_vdev->vdev_objmgr.ref_id_dbg, 345 QDF_TRACE_LEVEL_DEBUG); 346 break; 347 case WLAN_PEER_OP: 348 wlan_objmgr_print_ref_ids(obj->obj_peer->peer_objmgr.ref_id_dbg, 349 QDF_TRACE_LEVEL_DEBUG); 350 break; 351 default: 352 obj_mgr_debug("invalid obj_type"); 353 } 354 } 355 356 #ifdef WLAN_OBJMGR_REF_ID_TRACE 357 static void 358 wlan_objmgr_print_ref_func_line(struct wlan_objmgr_trace_func *func_head, 359 uint32_t id) 360 { 361 uint32_t ref_cnt; 362 struct wlan_objmgr_line_ref_node *tmp_ln_node; 363 364 obj_mgr_debug("ID: %s", string_from_dbgid(id)); 365 while (func_head) { 366 obj_mgr_debug("Func: %s", func_head->func); 367 tmp_ln_node = func_head->line_head; 368 while (tmp_ln_node) { 369 ref_cnt = qdf_atomic_read(&tmp_ln_node->line_ref.cnt); 370 obj_mgr_debug("line: %d cnt: %d", 371 tmp_ln_node->line_ref.line, 372 ref_cnt); 373 tmp_ln_node = tmp_ln_node->next; 374 } 375 func_head = func_head->next; 376 } 377 } 378 379 static void 380 wlan_objmgr_trace_print_ref(union wlan_objmgr_del_obj *obj, 381 enum wlan_objmgr_obj_type obj_type) 382 { 383 uint32_t id; 384 struct wlan_objmgr_trace_func *func_head; 385 struct wlan_objmgr_trace *trace; 386 struct wlan_objmgr_vdev_objmgr *vdev_obj; 387 struct wlan_objmgr_peer_objmgr *peer_obj; 388 389 switch (obj_type) { 390 case WLAN_VDEV_OP: 391 vdev_obj = &obj->obj_vdev->vdev_objmgr; 392 trace = &vdev_obj->trace; 393 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 394 if (qdf_atomic_read(&vdev_obj->ref_id_dbg[id])) { 395 obj_mgr_debug("Reference:"); 396 397 func_head = trace->references[id].head; 398 wlan_objmgr_print_ref_func_line(func_head, id); 399 400 obj_mgr_debug("Dereference:"); 401 func_head = trace->dereferences[id].head; 402 wlan_objmgr_print_ref_func_line(func_head, id); 403 } 404 } 405 break; 406 case WLAN_PEER_OP: 407 peer_obj = &obj->obj_peer->peer_objmgr; 408 trace = &peer_obj->trace; 409 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 410 if (qdf_atomic_read(&vdev_obj->ref_id_dbg[id])) { 411 obj_mgr_debug("Reference:"); 412 413 func_head = trace->references[id].head; 414 wlan_objmgr_print_ref_func_line(func_head, id); 415 416 obj_mgr_debug("Dereference:"); 417 func_head = trace->dereferences[id].head; 418 wlan_objmgr_print_ref_func_line(func_head, id); 419 } 420 } 421 break; 422 default: 423 break; 424 } 425 } 426 #else 427 static void 428 wlan_objmgr_trace_print_ref(union wlan_objmgr_del_obj *obj, 429 enum wlan_objmgr_obj_type obj_type) 430 { 431 } 432 #endif 433 434 /* timeout handler for iterating logically deleted object */ 435 436 static void wlan_objmgr_iterate_log_del_obj_handler(void *timer_arg) 437 { 438 enum wlan_objmgr_obj_type obj_type; 439 uint8_t *macaddr; 440 const char *obj_name; 441 struct wlan_objmgr_debug_info *debug_info; 442 qdf_list_node_t *node; 443 qdf_list_t *log_del_obj_list = NULL; 444 struct log_del_obj *del_obj = NULL; 445 qdf_time_t cur_tstamp; 446 QDF_STATUS status; 447 448 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 449 debug_info = g_umac_glb_obj->debug_info; 450 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 451 452 if (!debug_info) { 453 obj_mgr_err("debug_info is not initialized"); 454 return; 455 } 456 457 log_del_obj_list = &debug_info->obj_list; 458 qdf_spin_lock_bh(&debug_info->list_lock); 459 460 status = qdf_list_peek_front(log_del_obj_list, &node); 461 if (QDF_IS_STATUS_ERROR(status)) { 462 qdf_spin_unlock_bh(&debug_info->list_lock); 463 return; 464 } 465 466 /* compute the current timestamp in seconds 467 * need to compare with destroy duration of object 468 */ 469 cur_tstamp = (qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000); 470 471 do { 472 del_obj = qdf_container_of(node, struct log_del_obj, node); 473 obj_type = del_obj->obj_type; 474 macaddr = wlan_objmgr_debug_get_macaddr(&del_obj->obj, 475 obj_type); 476 obj_name = wlan_obj_type_get_obj_name(obj_type); 477 478 /* If object is in logically deleted state for time more than 479 * destroy duration, print the object type and MAC 480 */ 481 if (cur_tstamp < (del_obj->tstamp + 482 LOG_DEL_OBJ_DESTROY_DURATION_SEC)) { 483 break; 484 } 485 if (!macaddr) { 486 obj_mgr_err("macaddr is null"); 487 QDF_BUG(0); 488 break; 489 } 490 if (!obj_name) { 491 obj_mgr_err("obj_name is null"); 492 QDF_BUG(0); 493 break; 494 } 495 496 obj_mgr_alert("#%s in L-state,MAC: " QDF_MAC_ADDR_STR, 497 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 498 wlan_objmgr_print_pending_refs(&del_obj->obj, obj_type); 499 500 wlan_objmgr_trace_print_ref(&del_obj->obj, obj_type); 501 if (cur_tstamp > del_obj->tstamp + 502 LOG_DEL_OBJ_DESTROY_ASSERT_DURATION_SEC) { 503 if (!qdf_is_recovering() && !qdf_is_fw_down()) 504 wlan_objmgr_debug_obj_destroyed_panic(obj_name); 505 } 506 507 status = qdf_list_peek_next(log_del_obj_list, node, &node); 508 509 } while (QDF_IS_STATUS_SUCCESS(status)); 510 511 qdf_timer_mod(&debug_info->obj_timer, LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC); 512 qdf_spin_unlock_bh(&debug_info->list_lock); 513 } 514 515 void wlan_objmgr_debug_info_deinit(void) 516 { 517 struct log_del_obj *obj_to_remove; 518 struct wlan_objmgr_debug_info *debug_info; 519 qdf_list_node_t *node = NULL; 520 qdf_list_t *list; 521 bool is_child_alive = false; 522 523 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 524 debug_info = g_umac_glb_obj->debug_info; 525 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 526 527 if (!debug_info) { 528 obj_mgr_err("debug_info is not initialized"); 529 return; 530 } 531 list = &debug_info->obj_list; 532 533 qdf_spin_lock_bh(&debug_info->list_lock); 534 535 /* Check if any child of global object is in L-state and remove it, 536 * ideally it shouldn't be 537 */ 538 while (qdf_list_remove_front(list, &node) == QDF_STATUS_SUCCESS) { 539 is_child_alive = true; 540 obj_to_remove = qdf_container_of(node, 541 struct log_del_obj, node); 542 if (qdf_list_empty(&debug_info->obj_list)) 543 qdf_timer_stop(&debug_info->obj_timer); 544 /* free the object */ 545 qdf_mem_free(obj_to_remove); 546 } 547 qdf_spin_unlock_bh(&debug_info->list_lock); 548 549 if (is_child_alive) { 550 obj_mgr_alert("This shouldn't happen!!, No child of global" 551 "object should be in L-state, as global obj" 552 "is going to destroy"); 553 QDF_BUG(0); 554 } 555 556 /* free timer, destroy spinlock, list and debug_info object as 557 * global object is going to free 558 */ 559 qdf_list_destroy(list); 560 qdf_timer_free(&debug_info->obj_timer); 561 qdf_spinlock_destroy(&debug_info->list_lock); 562 qdf_mem_free(debug_info); 563 564 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 565 g_umac_glb_obj->debug_info = NULL; 566 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 567 } 568 569 void wlan_objmgr_debug_info_init(void) 570 { 571 struct wlan_objmgr_debug_info *debug_info; 572 573 debug_info = qdf_mem_malloc(sizeof(*debug_info)); 574 if (!debug_info) { 575 g_umac_glb_obj->debug_info = NULL; 576 return; 577 } 578 579 /* Initialize timer with timeout handler */ 580 qdf_timer_init(NULL, &debug_info->obj_timer, 581 wlan_objmgr_iterate_log_del_obj_handler, 582 NULL, QDF_TIMER_TYPE_WAKE_APPS); 583 584 /* Initialze the node_count to 0 and create list*/ 585 qdf_list_create(&debug_info->obj_list, 586 LOG_DEL_OBJ_LIST_MAX_COUNT); 587 588 /* Initialize the spin_lock to protect list */ 589 qdf_spinlock_create(&debug_info->list_lock); 590 591 /* attach debug_info object to global object */ 592 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 593 g_umac_glb_obj->debug_info = debug_info; 594 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 595 } 596 597 #ifdef WLAN_OBJMGR_REF_ID_TRACE 598 void 599 wlan_objmgr_trace_init_lock(struct wlan_objmgr_trace *trace) 600 { 601 qdf_spinlock_create(&trace->trace_lock); 602 } 603 604 void 605 wlan_objmgr_trace_deinit_lock(struct wlan_objmgr_trace *trace) 606 { 607 qdf_spinlock_destroy(&trace->trace_lock); 608 } 609 #endif 610 611 #ifdef WLAN_OBJMGR_REF_ID_TRACE 612 static inline struct wlan_objmgr_line_ref_node* 613 wlan_objmgr_trace_line_node_alloc(int line) 614 { 615 struct wlan_objmgr_line_ref_node *line_node; 616 617 line_node = qdf_mem_malloc_atomic(sizeof(*line_node)); 618 if (!line_node) 619 return NULL; 620 621 line_node->line_ref.line = line; 622 qdf_atomic_set(&line_node->line_ref.cnt, 1); 623 line_node->next = NULL; 624 625 return line_node; 626 } 627 628 static inline struct wlan_objmgr_trace_func* 629 wlan_objmgr_trace_ref_node_alloc(const char *func, int line) 630 { 631 struct wlan_objmgr_trace_func *func_node; 632 struct wlan_objmgr_line_ref_node *line_node; 633 634 func_node = qdf_mem_malloc_atomic(sizeof(*func_node)); 635 if (!func_node) 636 return NULL; 637 638 line_node = wlan_objmgr_trace_line_node_alloc(line); 639 if (!line_node) { 640 qdf_mem_free(func_node); 641 return NULL; 642 } 643 644 func_node->line_head = line_node; 645 qdf_str_lcopy(func_node->func, func, WLAN_OBJMGR_TRACE_FUNC_SIZE); 646 func_node->next = NULL; 647 648 return func_node; 649 } 650 651 static inline void 652 wlan_objmgr_trace_check_line(struct wlan_objmgr_trace_func *tmp_func_node, 653 struct wlan_objmgr_trace *trace, int line) 654 { 655 struct wlan_objmgr_line_ref_node *line_node; 656 struct wlan_objmgr_line_ref_node *tmp_ln_node; 657 658 tmp_ln_node = tmp_func_node->line_head; 659 while (tmp_ln_node) { 660 line_node = tmp_ln_node; 661 if (tmp_ln_node->line_ref.line == line) { 662 qdf_atomic_inc(&tmp_ln_node->line_ref.cnt); 663 break; 664 } 665 tmp_ln_node = tmp_ln_node->next; 666 } 667 if (!tmp_ln_node) { 668 tmp_ln_node = wlan_objmgr_trace_line_node_alloc(line); 669 if (tmp_ln_node) 670 line_node->next = tmp_ln_node; 671 } 672 } 673 674 void 675 wlan_objmgr_trace_ref(struct wlan_objmgr_trace_func **func_head, 676 struct wlan_objmgr_trace *trace, 677 const char *func, int line) 678 { 679 struct wlan_objmgr_trace_func *tmp_func_node; 680 struct wlan_objmgr_trace_func *func_node; 681 682 qdf_spin_lock_bh(&trace->trace_lock); 683 if (!*func_head) { 684 tmp_func_node = wlan_objmgr_trace_ref_node_alloc(func, line); 685 if (tmp_func_node) 686 *func_head = tmp_func_node; 687 } else { 688 tmp_func_node = *func_head; 689 while (tmp_func_node) { 690 func_node = tmp_func_node; 691 if (!qdf_str_ncmp(tmp_func_node->func, func, 692 WLAN_OBJMGR_TRACE_FUNC_SIZE - 1)) { 693 wlan_objmgr_trace_check_line(tmp_func_node, 694 trace, line); 695 break; 696 } 697 tmp_func_node = tmp_func_node->next; 698 } 699 700 if (!tmp_func_node) { 701 tmp_func_node = wlan_objmgr_trace_ref_node_alloc(func, 702 line); 703 if (tmp_func_node) 704 func_node->next = tmp_func_node; 705 } 706 } 707 qdf_spin_unlock_bh(&trace->trace_lock); 708 } 709 710 void 711 wlan_objmgr_trace_del_line(struct wlan_objmgr_line_ref_node **line_head) 712 { 713 struct wlan_objmgr_line_ref_node *del_tmp_node; 714 struct wlan_objmgr_line_ref_node *line_node; 715 716 line_node = *line_head; 717 while (line_node) { 718 del_tmp_node = line_node; 719 line_node = line_node->next; 720 qdf_mem_free(del_tmp_node); 721 } 722 *line_head = NULL; 723 } 724 725 void 726 wlan_objmgr_trace_del_ref_list(struct wlan_objmgr_trace *trace) 727 { 728 struct wlan_objmgr_trace_func *func_node; 729 struct wlan_objmgr_trace_func *del_tmp_node; 730 uint32_t id; 731 732 qdf_spin_lock_bh(&trace->trace_lock); 733 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 734 func_node = trace->references[id].head; 735 while (func_node) { 736 del_tmp_node = func_node; 737 wlan_objmgr_trace_del_line(&del_tmp_node->line_head); 738 func_node = func_node->next; 739 qdf_mem_free(del_tmp_node); 740 } 741 trace->references[id].head = NULL; 742 } 743 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 744 func_node = trace->dereferences[id].head; 745 while (func_node) { 746 del_tmp_node = func_node; 747 wlan_objmgr_trace_del_line(&del_tmp_node->line_head); 748 func_node = func_node->next; 749 qdf_mem_free(del_tmp_node); 750 } 751 trace->dereferences[id].head = NULL; 752 } 753 qdf_spin_unlock_bh(&trace->trace_lock); 754 } 755 #endif 756