1 /* 2 * 3 * Copyright (c) 2018 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 obj_mgr_err("Object node creation failed"); 159 return; 160 } 161 node->obj = obj; 162 node->obj_type = obj_type; 163 node->tstamp = tstamp; 164 obj_mgr_alert("#%s : mac_addr :" QDF_MAC_ADDR_STR" entered L-state", 165 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 166 wlan_objmgr_insert_ld_obj_to_list(debug_info, &node->node); 167 } 168 169 static void 170 wlan_objmgr_rem_ld_obj_from_list(void *obj, 171 struct wlan_objmgr_debug_info *debug_info, 172 enum wlan_objmgr_obj_type obj_type) 173 { 174 qdf_list_node_t *node = NULL; 175 struct log_del_obj *obj_to_remove = NULL; 176 qdf_list_t *list; 177 QDF_STATUS status; 178 179 list = &debug_info->obj_list; 180 qdf_spin_lock_bh(&debug_info->list_lock); 181 status = qdf_list_peek_front(list, &node); 182 183 while (QDF_IS_STATUS_SUCCESS(status)) { 184 obj_to_remove = qdf_container_of(node, 185 struct log_del_obj, node); 186 if (obj_to_remove->obj == obj && 187 obj_to_remove->obj_type == obj_type) { 188 status = qdf_list_remove_node(list, 189 &obj_to_remove->node); 190 /* Stop timer if list is empty */ 191 if (QDF_IS_STATUS_SUCCESS(status)) { 192 if (qdf_list_empty(&debug_info->obj_list)) 193 qdf_timer_stop(&debug_info->obj_timer); 194 qdf_mem_free(obj_to_remove); 195 } 196 break; 197 } 198 status = qdf_list_peek_next(list, node, &node); 199 }; 200 qdf_spin_unlock_bh(&debug_info->list_lock); 201 } 202 203 void wlan_objmgr_notify_destroy(void *obj, 204 enum wlan_objmgr_obj_type obj_type) 205 { 206 struct wlan_objmgr_debug_info *debug_info; 207 uint8_t *macaddr; 208 const char *obj_name; 209 210 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 211 debug_info = g_umac_glb_obj->debug_info; 212 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 213 214 if (!debug_info) { 215 obj_mgr_err("debug_info is null"); 216 return; 217 } 218 macaddr = wlan_objmgr_debug_get_macaddr(obj, obj_type); 219 if (!macaddr) { 220 obj_mgr_err("macaddr is null"); 221 return; 222 } 223 obj_name = wlan_obj_type_get_obj_name(obj_type); 224 if (!obj_name) { 225 obj_mgr_err("obj_name is null"); 226 return; 227 } 228 obj_mgr_alert("#%s, macaddr: " QDF_MAC_ADDR_STR" exited L-state", 229 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 230 231 wlan_objmgr_rem_ld_obj_from_list(obj, debug_info, obj_type); 232 } 233 234 /* timeout handler for iterating logically deleted object */ 235 236 static void wlan_objmgr_iterate_log_del_obj_handler(void *timer_arg) 237 { 238 enum wlan_objmgr_obj_type obj_type; 239 uint8_t *macaddr; 240 const char *obj_name; 241 struct wlan_objmgr_debug_info *debug_info; 242 qdf_list_node_t *node; 243 qdf_list_t *log_del_obj_list = NULL; 244 struct log_del_obj *del_obj = NULL; 245 qdf_time_t cur_tstamp; 246 QDF_STATUS status; 247 248 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 249 debug_info = g_umac_glb_obj->debug_info; 250 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 251 252 if (!debug_info) { 253 obj_mgr_err("debug_info is not initialized"); 254 return; 255 } 256 257 log_del_obj_list = &debug_info->obj_list; 258 qdf_spin_lock_bh(&debug_info->list_lock); 259 260 status = qdf_list_peek_front(log_del_obj_list, &node); 261 if (QDF_IS_STATUS_ERROR(status)) { 262 qdf_spin_unlock_bh(&debug_info->list_lock); 263 return; 264 } 265 266 /* compute the current timestamp in seconds 267 * need to compare with destroy duration of object 268 */ 269 cur_tstamp = (qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000); 270 271 do { 272 del_obj = qdf_container_of(node, struct log_del_obj, node); 273 obj_type = del_obj->obj_type; 274 macaddr = wlan_objmgr_debug_get_macaddr(del_obj->obj, obj_type); 275 obj_name = wlan_obj_type_get_obj_name(obj_type); 276 277 /* If object is in logically deleted state for time more than 278 * destroy duration, print the object type and MAC 279 */ 280 if (cur_tstamp < (del_obj->tstamp + 281 LOG_DEL_OBJ_DESTROY_DURATION_SEC)) { 282 break; 283 } 284 if (!macaddr) { 285 obj_mgr_err("macaddr is null"); 286 return; 287 } 288 if (!obj_name) { 289 obj_mgr_err("obj_name is null"); 290 return; 291 } 292 293 obj_mgr_alert("#%s in L-state,MAC: " QDF_MAC_ADDR_STR, 294 obj_name, QDF_MAC_ADDR_ARRAY(macaddr)); 295 296 status = qdf_list_peek_next(log_del_obj_list, node, &node); 297 298 } while (QDF_IS_STATUS_SUCCESS(status)); 299 300 qdf_spin_unlock_bh(&debug_info->list_lock); 301 302 /* modify timer timeout value */ 303 qdf_timer_mod(&debug_info->obj_timer, LOG_DEL_OBJ_TIMEOUT_VALUE_MSEC); 304 } 305 306 void wlan_objmgr_debug_info_deinit(void) 307 { 308 struct log_del_obj *obj_to_remove; 309 struct wlan_objmgr_debug_info *debug_info; 310 qdf_list_node_t *node = NULL; 311 qdf_list_t *list; 312 bool is_child_alive = false; 313 314 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 315 debug_info = g_umac_glb_obj->debug_info; 316 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 317 318 if (!debug_info) { 319 obj_mgr_err("debug_info is not initialized"); 320 return; 321 } 322 list = &debug_info->obj_list; 323 324 qdf_spin_lock_bh(&debug_info->list_lock); 325 326 /* Check if any child of global object is in L-state and remove it, 327 * ideally it shouldn't be 328 */ 329 while (qdf_list_remove_front(list, &node) == QDF_STATUS_SUCCESS) { 330 is_child_alive = true; 331 obj_to_remove = qdf_container_of(node, 332 struct log_del_obj, node); 333 if (qdf_list_empty(&debug_info->obj_list)) 334 qdf_timer_stop(&debug_info->obj_timer); 335 /* free the object */ 336 qdf_mem_free(obj_to_remove); 337 } 338 qdf_spin_unlock_bh(&debug_info->list_lock); 339 340 if (is_child_alive) { 341 obj_mgr_alert("This shouldn't happen!!, No child of global" 342 "object should be in L-state, as global obj" 343 "is going to destroy"); 344 QDF_BUG(0); 345 } 346 347 /* free timer, destroy spinlock, list and debug_info object as 348 * global object is going to free 349 */ 350 qdf_list_destroy(list); 351 qdf_timer_free(&debug_info->obj_timer); 352 qdf_spinlock_destroy(&debug_info->list_lock); 353 qdf_mem_free(debug_info); 354 355 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 356 g_umac_glb_obj->debug_info = NULL; 357 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 358 } 359 360 void wlan_objmgr_debug_info_init(void) 361 { 362 struct wlan_objmgr_debug_info *debug_info; 363 364 debug_info = qdf_mem_malloc(sizeof(*debug_info)); 365 if (!debug_info) { 366 obj_mgr_err("debug_info allocation failed"); 367 g_umac_glb_obj->debug_info = NULL; 368 return; 369 } 370 371 /* Initialize timer with timeout handler */ 372 qdf_timer_init(NULL, &debug_info->obj_timer, 373 wlan_objmgr_iterate_log_del_obj_handler, 374 NULL, QDF_TIMER_TYPE_WAKE_APPS); 375 376 /* Initialze the node_count to 0 and create list*/ 377 qdf_list_create(&debug_info->obj_list, 378 LOG_DEL_OBJ_LIST_MAX_COUNT); 379 380 /* Initialize the spin_lock to protect list */ 381 qdf_spinlock_create(&debug_info->list_lock); 382 383 /* attach debug_info object to global object */ 384 qdf_spin_lock_bh(&g_umac_glb_obj->global_lock); 385 g_umac_glb_obj->debug_info = debug_info; 386 qdf_spin_unlock_bh(&g_umac_glb_obj->global_lock); 387 } 388