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