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 30 #define LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC 5000 31 #define LOG_DEL_OBJ_DESTROY_DURATION_SEC 5 32 #define LOG_DEL_OBJ_LIST_MAX_COUNT (3 + 5 + 48 + 4096) 33 34 /** 35 * struct log_del_obj - Logically deleted Object 36 * @obj: Represents peer/vdev/pdev/psoc 37 * @node: List node from Logically deleted list 38 * @obj_type: Object type for peer/vdev/pdev/psoc 39 * @tstamp: Timestamp when node entered logically 40 * deleted state 41 */ 42 struct log_del_obj { 43 void *obj; 44 qdf_list_node_t node; 45 enum wlan_objmgr_obj_type obj_type; 46 qdf_time_t tstamp; 47 }; 48 49 /** 50 * struct wlan_objmgr_debug_info - Objmgr debug info 51 * for Logically deleted object 52 * @obj_timer: Timer object 53 * @obj_list: list object having linking logically 54 * deleted nodes 55 * @list_lock: lock to protect list 56 */ 57 struct wlan_objmgr_debug_info { 58 qdf_timer_t obj_timer; 59 qdf_list_t obj_list; 60 qdf_spinlock_t list_lock; 61 }; 62 63 static const char * 64 wlan_obj_type_get_obj_name(enum wlan_objmgr_obj_type obj_type) 65 { 66 static const struct wlan_obj_type_to_name { 67 enum wlan_objmgr_obj_type obj_type; 68 const char *name; 69 } obj_type_name[WLAN_OBJ_TYPE_MAX] = { 70 {WLAN_PSOC_OP, "psoc"}, 71 {WLAN_PDEV_OP, "pdev"}, 72 {WLAN_VDEV_OP, "vdev"}, 73 {WLAN_PEER_OP, "peer"} 74 }; 75 uint8_t idx; 76 77 for (idx = 0; idx < WLAN_OBJ_TYPE_MAX; idx++) { 78 if (obj_type == obj_type_name[idx].obj_type) 79 return obj_type_name[idx].name; 80 } 81 82 return NULL; 83 } 84 85 static uint8_t* 86 wlan_objmgr_debug_get_macaddr(void *obj, 87 enum wlan_objmgr_obj_type obj_type) 88 { 89 switch (obj_type) { 90 case WLAN_PSOC_OP: 91 return wlan_psoc_get_hw_macaddr(obj); 92 case WLAN_PDEV_OP: 93 return wlan_pdev_get_hw_macaddr(obj); 94 case WLAN_VDEV_OP: 95 return wlan_vdev_mlme_get_macaddr(obj); 96 case WLAN_PEER_OP: 97 return wlan_peer_get_macaddr(obj); 98 default: 99 obj_mgr_err("invalid obj_type"); 100 return NULL; 101 } 102 } 103 104 static void 105 wlan_objmgr_insert_ld_obj_to_list(struct wlan_objmgr_debug_info *debug_info, 106 qdf_list_node_t *node) 107 { 108 /* Insert object to list with lock being held*/ 109 qdf_spin_lock_bh(&debug_info->list_lock); 110 111 /* Start timer only when list is empty */ 112 if (qdf_list_empty(&debug_info->obj_list)) 113 qdf_timer_start(&debug_info->obj_timer, 114 LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC); 115 116 qdf_list_insert_back(&debug_info->obj_list, node); 117 qdf_spin_unlock_bh(&debug_info->list_lock); 118 } 119 120 void wlan_objmgr_notify_log_delete(void *obj, 121 enum wlan_objmgr_obj_type obj_type) 122 { 123 struct wlan_objmgr_debug_info *debug_info; 124 const char *obj_name; 125 uint8_t *macaddr; 126 qdf_time_t tstamp; 127 struct log_del_obj *node; 128 129 if (!obj) { 130 obj_mgr_err("object is null"); 131 return; 132 } 133 134 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 135 debug_info = g_umac_glb_obj->debug_info; 136 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 137 138 if (!debug_info) { 139 obj_mgr_err("debug_info is null"); 140 return; 141 } 142 143 macaddr = wlan_objmgr_debug_get_macaddr(obj, obj_type); 144 if (!macaddr) { 145 obj_mgr_err("macaddr is null"); 146 return; 147 } 148 149 obj_name = wlan_obj_type_get_obj_name(obj_type); 150 if (!obj_name) { 151 obj_mgr_err("obj_name is null"); 152 return; 153 } 154 155 tstamp = qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000; 156 node = qdf_mem_malloc(sizeof(*node)); 157 if (!node) 158 return; 159 160 node->obj = obj; 161 node->obj_type = obj_type; 162 node->tstamp = tstamp; 163 obj_mgr_debug("#%s : mac_addr :" QDF_MAC_ADDR_STR" entered L-state", 164 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 165 wlan_objmgr_insert_ld_obj_to_list(debug_info, &node->node); 166 } 167 168 static void 169 wlan_objmgr_rem_ld_obj_from_list(void *obj, 170 struct wlan_objmgr_debug_info *debug_info, 171 enum wlan_objmgr_obj_type obj_type) 172 { 173 qdf_list_node_t *node = NULL; 174 struct log_del_obj *obj_to_remove = NULL; 175 qdf_list_t *list; 176 QDF_STATUS status; 177 178 list = &debug_info->obj_list; 179 qdf_spin_lock_bh(&debug_info->list_lock); 180 status = qdf_list_peek_front(list, &node); 181 182 while (QDF_IS_STATUS_SUCCESS(status)) { 183 obj_to_remove = qdf_container_of(node, 184 struct log_del_obj, node); 185 if (obj_to_remove->obj == obj && 186 obj_to_remove->obj_type == obj_type) { 187 status = qdf_list_remove_node(list, 188 &obj_to_remove->node); 189 /* Stop timer if list is empty */ 190 if (QDF_IS_STATUS_SUCCESS(status)) { 191 if (qdf_list_empty(&debug_info->obj_list)) 192 qdf_timer_stop(&debug_info->obj_timer); 193 qdf_mem_free(obj_to_remove); 194 } 195 break; 196 } 197 status = qdf_list_peek_next(list, node, &node); 198 }; 199 qdf_spin_unlock_bh(&debug_info->list_lock); 200 } 201 202 void wlan_objmgr_notify_destroy(void *obj, 203 enum wlan_objmgr_obj_type obj_type) 204 { 205 struct wlan_objmgr_debug_info *debug_info; 206 uint8_t *macaddr; 207 const char *obj_name; 208 209 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 210 debug_info = g_umac_glb_obj->debug_info; 211 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 212 213 if (!debug_info) { 214 obj_mgr_err("debug_info is null"); 215 return; 216 } 217 macaddr = wlan_objmgr_debug_get_macaddr(obj, obj_type); 218 if (!macaddr) { 219 obj_mgr_err("macaddr is null"); 220 return; 221 } 222 obj_name = wlan_obj_type_get_obj_name(obj_type); 223 if (!obj_name) { 224 obj_mgr_err("obj_name is null"); 225 return; 226 } 227 obj_mgr_debug("#%s, macaddr: " QDF_MAC_ADDR_STR" exited L-state", 228 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 229 230 wlan_objmgr_rem_ld_obj_from_list(obj, debug_info, obj_type); 231 } 232 233 /* timeout handler for iterating logically deleted object */ 234 235 static void wlan_objmgr_iterate_log_del_obj_handler(void *timer_arg) 236 { 237 enum wlan_objmgr_obj_type obj_type; 238 uint8_t *macaddr; 239 const char *obj_name; 240 struct wlan_objmgr_debug_info *debug_info; 241 qdf_list_node_t *node; 242 qdf_list_t *log_del_obj_list = NULL; 243 struct log_del_obj *del_obj = NULL; 244 qdf_time_t cur_tstamp; 245 QDF_STATUS status; 246 247 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 248 debug_info = g_umac_glb_obj->debug_info; 249 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 250 251 if (!debug_info) { 252 obj_mgr_err("debug_info is not initialized"); 253 return; 254 } 255 256 log_del_obj_list = &debug_info->obj_list; 257 qdf_spin_lock_bh(&debug_info->list_lock); 258 259 status = qdf_list_peek_front(log_del_obj_list, &node); 260 if (QDF_IS_STATUS_ERROR(status)) { 261 qdf_spin_unlock_bh(&debug_info->list_lock); 262 return; 263 } 264 265 /* compute the current timestamp in seconds 266 * need to compare with destroy duration of object 267 */ 268 cur_tstamp = (qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000); 269 270 do { 271 del_obj = qdf_container_of(node, struct log_del_obj, node); 272 obj_type = del_obj->obj_type; 273 macaddr = wlan_objmgr_debug_get_macaddr(del_obj->obj, obj_type); 274 obj_name = wlan_obj_type_get_obj_name(obj_type); 275 276 /* If object is in logically deleted state for time more than 277 * destroy duration, print the object type and MAC 278 */ 279 if (cur_tstamp < (del_obj->tstamp + 280 LOG_DEL_OBJ_DESTROY_DURATION_SEC)) { 281 break; 282 } 283 if (!macaddr) { 284 obj_mgr_err("macaddr is null"); 285 QDF_BUG(0); 286 break; 287 } 288 if (!obj_name) { 289 obj_mgr_err("obj_name is null"); 290 QDF_BUG(0); 291 break; 292 } 293 294 obj_mgr_alert("#%s in L-state,MAC: " QDF_MAC_ADDR_STR, 295 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 296 297 status = qdf_list_peek_next(log_del_obj_list, node, &node); 298 299 } while (QDF_IS_STATUS_SUCCESS(status)); 300 301 qdf_timer_mod(&debug_info->obj_timer, LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC); 302 qdf_spin_unlock_bh(&debug_info->list_lock); 303 } 304 305 void wlan_objmgr_debug_info_deinit(void) 306 { 307 struct log_del_obj *obj_to_remove; 308 struct wlan_objmgr_debug_info *debug_info; 309 qdf_list_node_t *node = NULL; 310 qdf_list_t *list; 311 bool is_child_alive = false; 312 313 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 314 debug_info = g_umac_glb_obj->debug_info; 315 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 316 317 if (!debug_info) { 318 obj_mgr_err("debug_info is not initialized"); 319 return; 320 } 321 list = &debug_info->obj_list; 322 323 qdf_spin_lock_bh(&debug_info->list_lock); 324 325 /* Check if any child of global object is in L-state and remove it, 326 * ideally it shouldn't be 327 */ 328 while (qdf_list_remove_front(list, &node) == QDF_STATUS_SUCCESS) { 329 is_child_alive = true; 330 obj_to_remove = qdf_container_of(node, 331 struct log_del_obj, node); 332 if (qdf_list_empty(&debug_info->obj_list)) 333 qdf_timer_stop(&debug_info->obj_timer); 334 /* free the object */ 335 qdf_mem_free(obj_to_remove); 336 } 337 qdf_spin_unlock_bh(&debug_info->list_lock); 338 339 if (is_child_alive) { 340 obj_mgr_alert("This shouldn't happen!!, No child of global" 341 "object should be in L-state, as global obj" 342 "is going to destroy"); 343 QDF_BUG(0); 344 } 345 346 /* free timer, destroy spinlock, list and debug_info object as 347 * global object is going to free 348 */ 349 qdf_list_destroy(list); 350 qdf_timer_free(&debug_info->obj_timer); 351 qdf_spinlock_destroy(&debug_info->list_lock); 352 qdf_mem_free(debug_info); 353 354 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 355 g_umac_glb_obj->debug_info = NULL; 356 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 357 } 358 359 void wlan_objmgr_debug_info_init(void) 360 { 361 struct wlan_objmgr_debug_info *debug_info; 362 363 debug_info = qdf_mem_malloc(sizeof(*debug_info)); 364 if (!debug_info) { 365 g_umac_glb_obj->debug_info = NULL; 366 return; 367 } 368 369 /* Initialize timer with timeout handler */ 370 qdf_timer_init(NULL, &debug_info->obj_timer, 371 wlan_objmgr_iterate_log_del_obj_handler, 372 NULL, QDF_TIMER_TYPE_WAKE_APPS); 373 374 /* Initialze the node_count to 0 and create list*/ 375 qdf_list_create(&debug_info->obj_list, 376 LOG_DEL_OBJ_LIST_MAX_COUNT); 377 378 /* Initialize the spin_lock to protect list */ 379 qdf_spinlock_create(&debug_info->list_lock); 380 381 /* attach debug_info object to global object */ 382 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 383 g_umac_glb_obj->debug_info = debug_info; 384 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 385 } 386