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_FMT" entered L-state", 207 obj_name, QDF_MAC_ADDR_REF(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 return (del_obj->obj_psoc == obj->obj_psoc); 218 case WLAN_PDEV_OP: 219 return (del_obj->obj_pdev == obj->obj_pdev); 220 case WLAN_VDEV_OP: 221 return (del_obj->obj_vdev == obj->obj_vdev); 222 case WLAN_PEER_OP: 223 return (del_obj->obj_peer == obj->obj_peer); 224 default: 225 return false; 226 } 227 } 228 229 static void 230 wlan_objmgr_rem_ld_obj_from_list(union wlan_objmgr_del_obj *obj, 231 struct wlan_objmgr_debug_info *debug_info, 232 enum wlan_objmgr_obj_type obj_type) 233 { 234 qdf_list_node_t *node = NULL; 235 struct log_del_obj *obj_to_remove = NULL; 236 qdf_list_t *list; 237 QDF_STATUS status; 238 239 list = &debug_info->obj_list; 240 qdf_spin_lock_bh(&debug_info->list_lock); 241 status = qdf_list_peek_front(list, &node); 242 243 while (QDF_IS_STATUS_SUCCESS(status)) { 244 obj_to_remove = qdf_container_of(node, 245 struct log_del_obj, node); 246 if (wlan_objmgr_del_obj_match(obj, &obj_to_remove->obj, 247 obj_type) && 248 obj_to_remove->obj_type == obj_type) { 249 status = qdf_list_remove_node(list, 250 &obj_to_remove->node); 251 /* Stop timer if list is empty */ 252 if (QDF_IS_STATUS_SUCCESS(status)) { 253 if (qdf_list_empty(&debug_info->obj_list)) 254 qdf_timer_stop(&debug_info->obj_timer); 255 qdf_mem_free(obj_to_remove); 256 } 257 break; 258 } 259 status = qdf_list_peek_next(list, node, &node); 260 }; 261 qdf_spin_unlock_bh(&debug_info->list_lock); 262 } 263 264 void wlan_objmgr_notify_destroy(void *obj, 265 enum wlan_objmgr_obj_type obj_type) 266 { 267 struct wlan_objmgr_debug_info *debug_info; 268 uint8_t *macaddr; 269 const char *obj_name; 270 union wlan_objmgr_del_obj *del_obj = (union wlan_objmgr_del_obj *)&obj; 271 272 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 273 debug_info = g_umac_glb_obj->debug_info; 274 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 275 276 if (!debug_info) { 277 obj_mgr_err("debug_info is null"); 278 return; 279 } 280 macaddr = wlan_objmgr_debug_get_macaddr(del_obj, obj_type); 281 if (!macaddr) { 282 obj_mgr_err("macaddr is null"); 283 return; 284 } 285 obj_name = wlan_obj_type_get_obj_name(obj_type); 286 if (!obj_name) { 287 obj_mgr_err("obj_name is null"); 288 return; 289 } 290 obj_mgr_debug("#%s : macaddr: "QDF_MAC_ADDR_FMT" exited L-state", 291 obj_name, QDF_MAC_ADDR_REF(macaddr)); 292 293 wlan_objmgr_rem_ld_obj_from_list(del_obj, 294 debug_info, obj_type); 295 } 296 297 /** 298 * wlan_objmgr_debug_obj_destroyed_panic() - Panic in case obj is in L-state 299 * for long 300 * @obj_name: The name of the module ID 301 * 302 * This will invoke panic in the case that the obj is in logically destroyed 303 * state for a long time. The panic is invoked only in case feature flag 304 * WLAN_OBJMGR_PANIC_ON_BUG is enabled 305 * 306 * Return: None 307 */ 308 #ifdef CONFIG_LEAK_DETECTION 309 static inline void wlan_objmgr_debug_obj_destroyed_panic(const char *obj_name) 310 { 311 obj_mgr_alert("#%s in L-state for too long!", obj_name); 312 QDF_BUG(0); 313 } 314 #else 315 static inline void wlan_objmgr_debug_obj_destroyed_panic(const char *obj_name) 316 { 317 } 318 #endif 319 320 /* 321 * wlan_objmgr_print_pending_refs() - Print pending refs according to the obj 322 * @obj: Represents peer/vdev/pdev/psoc 323 * @obj_type: Object type for peer/vdev/pdev/psoc 324 * 325 * Return: None 326 */ 327 static void wlan_objmgr_print_pending_refs(union wlan_objmgr_del_obj *obj, 328 enum wlan_objmgr_obj_type obj_type) 329 { 330 switch (obj_type) { 331 case WLAN_PSOC_OP: 332 wlan_objmgr_print_ref_ids(obj->obj_psoc->soc_objmgr.ref_id_dbg, 333 QDF_TRACE_LEVEL_DEBUG); 334 break; 335 case WLAN_PDEV_OP: 336 wlan_objmgr_print_ref_ids(obj->obj_pdev->pdev_objmgr.ref_id_dbg, 337 QDF_TRACE_LEVEL_DEBUG); 338 break; 339 case WLAN_VDEV_OP: 340 wlan_objmgr_print_ref_ids(obj->obj_vdev->vdev_objmgr.ref_id_dbg, 341 QDF_TRACE_LEVEL_DEBUG); 342 break; 343 case WLAN_PEER_OP: 344 wlan_objmgr_print_ref_ids(obj->obj_peer->peer_objmgr.ref_id_dbg, 345 QDF_TRACE_LEVEL_DEBUG); 346 break; 347 default: 348 obj_mgr_debug("invalid obj_type"); 349 } 350 } 351 352 #ifdef WLAN_OBJMGR_REF_ID_TRACE 353 static void 354 wlan_objmgr_print_ref_func_line(struct wlan_objmgr_trace_func *func_head, 355 uint32_t id) 356 { 357 uint32_t ref_cnt; 358 struct wlan_objmgr_line_ref_node *tmp_ln_node; 359 360 obj_mgr_debug("ID: %s", string_from_dbgid(id)); 361 while (func_head) { 362 obj_mgr_debug("Func: %s", func_head->func); 363 tmp_ln_node = func_head->line_head; 364 while (tmp_ln_node) { 365 ref_cnt = qdf_atomic_read(&tmp_ln_node->line_ref.cnt); 366 obj_mgr_debug("line: %d cnt: %d", 367 tmp_ln_node->line_ref.line, 368 ref_cnt); 369 tmp_ln_node = tmp_ln_node->next; 370 } 371 func_head = func_head->next; 372 } 373 } 374 375 static void 376 wlan_objmgr_trace_print_ref(union wlan_objmgr_del_obj *obj, 377 enum wlan_objmgr_obj_type obj_type) 378 { 379 uint32_t id; 380 struct wlan_objmgr_trace_func *func_head; 381 struct wlan_objmgr_trace *trace; 382 struct wlan_objmgr_vdev_objmgr *vdev_obj; 383 struct wlan_objmgr_peer_objmgr *peer_obj; 384 385 switch (obj_type) { 386 case WLAN_VDEV_OP: 387 vdev_obj = &obj->obj_vdev->vdev_objmgr; 388 trace = &vdev_obj->trace; 389 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 390 if (qdf_atomic_read(&vdev_obj->ref_id_dbg[id])) { 391 obj_mgr_debug("Reference:"); 392 393 func_head = trace->references[id].head; 394 wlan_objmgr_print_ref_func_line(func_head, id); 395 396 obj_mgr_debug("Dereference:"); 397 func_head = trace->dereferences[id].head; 398 wlan_objmgr_print_ref_func_line(func_head, id); 399 } 400 } 401 break; 402 case WLAN_PEER_OP: 403 peer_obj = &obj->obj_peer->peer_objmgr; 404 trace = &peer_obj->trace; 405 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 406 if (qdf_atomic_read(&peer_obj->ref_id_dbg[id])) { 407 obj_mgr_debug("Reference:"); 408 409 func_head = trace->references[id].head; 410 wlan_objmgr_print_ref_func_line(func_head, id); 411 412 obj_mgr_debug("Dereference:"); 413 func_head = trace->dereferences[id].head; 414 wlan_objmgr_print_ref_func_line(func_head, id); 415 } 416 } 417 break; 418 default: 419 break; 420 } 421 } 422 #else 423 static void 424 wlan_objmgr_trace_print_ref(union wlan_objmgr_del_obj *obj, 425 enum wlan_objmgr_obj_type obj_type) 426 { 427 } 428 #endif 429 430 /* timeout handler for iterating logically deleted object */ 431 432 static void wlan_objmgr_iterate_log_del_obj_handler(void *timer_arg) 433 { 434 enum wlan_objmgr_obj_type obj_type; 435 uint8_t *macaddr; 436 const char *obj_name; 437 struct wlan_objmgr_debug_info *debug_info; 438 qdf_list_node_t *node; 439 qdf_list_t *log_del_obj_list = NULL; 440 struct log_del_obj *del_obj = NULL; 441 qdf_time_t cur_tstamp; 442 QDF_STATUS status; 443 444 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 445 debug_info = g_umac_glb_obj->debug_info; 446 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 447 448 if (!debug_info) { 449 obj_mgr_err("debug_info is not initialized"); 450 return; 451 } 452 453 log_del_obj_list = &debug_info->obj_list; 454 qdf_spin_lock_bh(&debug_info->list_lock); 455 456 status = qdf_list_peek_front(log_del_obj_list, &node); 457 if (QDF_IS_STATUS_ERROR(status)) { 458 qdf_spin_unlock_bh(&debug_info->list_lock); 459 return; 460 } 461 462 /* compute the current timestamp in seconds 463 * need to compare with destroy duration of object 464 */ 465 cur_tstamp = (qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000); 466 467 do { 468 del_obj = qdf_container_of(node, struct log_del_obj, node); 469 obj_type = del_obj->obj_type; 470 macaddr = wlan_objmgr_debug_get_macaddr(&del_obj->obj, 471 obj_type); 472 obj_name = wlan_obj_type_get_obj_name(obj_type); 473 474 /* If object is in logically deleted state for time more than 475 * destroy duration, print the object type and MAC 476 */ 477 if (cur_tstamp < (del_obj->tstamp + 478 LOG_DEL_OBJ_DESTROY_DURATION_SEC)) { 479 break; 480 } 481 if (!macaddr) { 482 obj_mgr_err("macaddr is null"); 483 QDF_BUG(0); 484 break; 485 } 486 if (!obj_name) { 487 obj_mgr_err("obj_name is null"); 488 QDF_BUG(0); 489 break; 490 } 491 492 obj_mgr_alert("#%s in L-state,MAC: " QDF_MAC_ADDR_FMT, 493 obj_name, QDF_MAC_ADDR_REF(macaddr)); 494 wlan_objmgr_print_pending_refs(&del_obj->obj, obj_type); 495 496 wlan_objmgr_trace_print_ref(&del_obj->obj, obj_type); 497 if (cur_tstamp > del_obj->tstamp + 498 LOG_DEL_OBJ_DESTROY_ASSERT_DURATION_SEC) { 499 if (!qdf_is_recovering() && !qdf_is_fw_down()) 500 wlan_objmgr_debug_obj_destroyed_panic(obj_name); 501 } 502 503 status = qdf_list_peek_next(log_del_obj_list, node, &node); 504 505 } while (QDF_IS_STATUS_SUCCESS(status)); 506 507 qdf_timer_mod(&debug_info->obj_timer, LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC); 508 qdf_spin_unlock_bh(&debug_info->list_lock); 509 } 510 511 void wlan_objmgr_debug_info_deinit(void) 512 { 513 struct log_del_obj *obj_to_remove; 514 struct wlan_objmgr_debug_info *debug_info; 515 qdf_list_node_t *node = NULL; 516 qdf_list_t *list; 517 bool is_child_alive = false; 518 519 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 520 debug_info = g_umac_glb_obj->debug_info; 521 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 522 523 if (!debug_info) { 524 obj_mgr_err("debug_info is not initialized"); 525 return; 526 } 527 list = &debug_info->obj_list; 528 529 qdf_spin_lock_bh(&debug_info->list_lock); 530 531 /* Check if any child of global object is in L-state and remove it, 532 * ideally it shouldn't be 533 */ 534 while (qdf_list_remove_front(list, &node) == QDF_STATUS_SUCCESS) { 535 is_child_alive = true; 536 obj_to_remove = qdf_container_of(node, 537 struct log_del_obj, node); 538 if (qdf_list_empty(&debug_info->obj_list)) 539 qdf_timer_stop(&debug_info->obj_timer); 540 /* free the object */ 541 qdf_mem_free(obj_to_remove); 542 } 543 qdf_spin_unlock_bh(&debug_info->list_lock); 544 545 if (is_child_alive) { 546 obj_mgr_alert("This shouldn't happen!!, No child of global" 547 "object should be in L-state, as global obj" 548 "is going to destroy"); 549 QDF_BUG(0); 550 } 551 552 /* free timer, destroy spinlock, list and debug_info object as 553 * global object is going to free 554 */ 555 qdf_list_destroy(list); 556 qdf_timer_free(&debug_info->obj_timer); 557 qdf_spinlock_destroy(&debug_info->list_lock); 558 qdf_mem_free(debug_info); 559 560 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 561 g_umac_glb_obj->debug_info = NULL; 562 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 563 } 564 565 void wlan_objmgr_debug_info_init(void) 566 { 567 struct wlan_objmgr_debug_info *debug_info; 568 569 debug_info = qdf_mem_malloc(sizeof(*debug_info)); 570 if (!debug_info) { 571 g_umac_glb_obj->debug_info = NULL; 572 return; 573 } 574 575 /* Initialize timer with timeout handler */ 576 qdf_timer_init(NULL, &debug_info->obj_timer, 577 wlan_objmgr_iterate_log_del_obj_handler, 578 NULL, QDF_TIMER_TYPE_WAKE_APPS); 579 580 /* Initialze the node_count to 0 and create list*/ 581 qdf_list_create(&debug_info->obj_list, 582 LOG_DEL_OBJ_LIST_MAX_COUNT); 583 584 /* Initialize the spin_lock to protect list */ 585 qdf_spinlock_create(&debug_info->list_lock); 586 587 /* attach debug_info object to global object */ 588 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 589 g_umac_glb_obj->debug_info = debug_info; 590 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 591 } 592 593 #ifdef WLAN_OBJMGR_REF_ID_TRACE 594 void 595 wlan_objmgr_trace_init_lock(struct wlan_objmgr_trace *trace) 596 { 597 qdf_spinlock_create(&trace->trace_lock); 598 } 599 600 void 601 wlan_objmgr_trace_deinit_lock(struct wlan_objmgr_trace *trace) 602 { 603 qdf_spinlock_destroy(&trace->trace_lock); 604 } 605 #endif 606 607 #ifdef WLAN_OBJMGR_REF_ID_TRACE 608 static inline struct wlan_objmgr_line_ref_node* 609 wlan_objmgr_trace_line_node_alloc(int line) 610 { 611 struct wlan_objmgr_line_ref_node *line_node; 612 613 line_node = qdf_mem_malloc_atomic(sizeof(*line_node)); 614 if (!line_node) 615 return NULL; 616 617 line_node->line_ref.line = line; 618 qdf_atomic_set(&line_node->line_ref.cnt, 1); 619 line_node->next = NULL; 620 621 return line_node; 622 } 623 624 static inline struct wlan_objmgr_trace_func* 625 wlan_objmgr_trace_ref_node_alloc(const char *func, int line) 626 { 627 struct wlan_objmgr_trace_func *func_node; 628 struct wlan_objmgr_line_ref_node *line_node; 629 630 func_node = qdf_mem_malloc_atomic(sizeof(*func_node)); 631 if (!func_node) 632 return NULL; 633 634 line_node = wlan_objmgr_trace_line_node_alloc(line); 635 if (!line_node) { 636 qdf_mem_free(func_node); 637 return NULL; 638 } 639 640 func_node->line_head = line_node; 641 qdf_str_lcopy(func_node->func, func, WLAN_OBJMGR_TRACE_FUNC_SIZE); 642 func_node->next = NULL; 643 644 return func_node; 645 } 646 647 static inline void 648 wlan_objmgr_trace_check_line(struct wlan_objmgr_trace_func *tmp_func_node, 649 struct wlan_objmgr_trace *trace, int line) 650 { 651 struct wlan_objmgr_line_ref_node *tmp_ln_node; 652 653 tmp_ln_node = tmp_func_node->line_head; 654 while (tmp_ln_node) { 655 if (tmp_ln_node->line_ref.line == line) { 656 qdf_atomic_inc(&tmp_ln_node->line_ref.cnt); 657 break; 658 } 659 tmp_ln_node = tmp_ln_node->next; 660 } 661 if (!tmp_ln_node) { 662 tmp_ln_node = wlan_objmgr_trace_line_node_alloc(line); 663 if (tmp_ln_node) { 664 tmp_ln_node->next = tmp_func_node->line_head; 665 tmp_func_node->line_head = tmp_ln_node; 666 } 667 } 668 } 669 670 void 671 wlan_objmgr_trace_ref(struct wlan_objmgr_trace_func **func_head, 672 struct wlan_objmgr_trace *trace, 673 const char *func, int line) 674 { 675 struct wlan_objmgr_trace_func *tmp_func_node; 676 struct wlan_objmgr_trace_func *func_node; 677 678 qdf_spin_lock_bh(&trace->trace_lock); 679 if (!*func_head) { 680 tmp_func_node = wlan_objmgr_trace_ref_node_alloc(func, line); 681 if (tmp_func_node) 682 *func_head = tmp_func_node; 683 } else { 684 tmp_func_node = *func_head; 685 while (tmp_func_node) { 686 func_node = tmp_func_node; 687 if (!qdf_str_ncmp(tmp_func_node->func, func, 688 WLAN_OBJMGR_TRACE_FUNC_SIZE - 1)) { 689 wlan_objmgr_trace_check_line(tmp_func_node, 690 trace, line); 691 break; 692 } 693 tmp_func_node = tmp_func_node->next; 694 } 695 696 if (!tmp_func_node) { 697 tmp_func_node = wlan_objmgr_trace_ref_node_alloc(func, 698 line); 699 if (tmp_func_node) 700 func_node->next = tmp_func_node; 701 } 702 } 703 qdf_spin_unlock_bh(&trace->trace_lock); 704 } 705 706 static void 707 wlan_objmgr_trace_del_line(struct wlan_objmgr_line_ref_node **line_head) 708 { 709 struct wlan_objmgr_line_ref_node *del_tmp_node; 710 struct wlan_objmgr_line_ref_node *line_node; 711 712 line_node = *line_head; 713 while (line_node) { 714 del_tmp_node = line_node; 715 line_node = line_node->next; 716 qdf_mem_free(del_tmp_node); 717 } 718 *line_head = NULL; 719 } 720 721 void 722 wlan_objmgr_trace_del_ref_list(struct wlan_objmgr_trace *trace) 723 { 724 struct wlan_objmgr_trace_func *func_node; 725 struct wlan_objmgr_trace_func *del_tmp_node; 726 uint32_t id; 727 728 qdf_spin_lock_bh(&trace->trace_lock); 729 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 730 func_node = trace->references[id].head; 731 while (func_node) { 732 del_tmp_node = func_node; 733 wlan_objmgr_trace_del_line(&del_tmp_node->line_head); 734 func_node = func_node->next; 735 qdf_mem_free(del_tmp_node); 736 } 737 trace->references[id].head = NULL; 738 } 739 for (id = 0; id < WLAN_REF_ID_MAX; id++) { 740 func_node = trace->dereferences[id].head; 741 while (func_node) { 742 del_tmp_node = func_node; 743 wlan_objmgr_trace_del_line(&del_tmp_node->line_head); 744 func_node = func_node->next; 745 qdf_mem_free(del_tmp_node); 746 } 747 trace->dereferences[id].head = NULL; 748 } 749 qdf_spin_unlock_bh(&trace->trace_lock); 750 } 751 #endif 752