1 /* 2 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. 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 #include "qdf_list.h" 21 #include "qdf_mem.h" 22 #include "qdf_status.h" 23 #include "qdf_str.h" 24 #include "qdf_threads.h" 25 #include "qdf_timer.h" 26 #include "__wlan_dsc.h" 27 #include "cds_api.h" 28 29 #ifdef WLAN_DSC_DEBUG __dsc_dbg_op_timeout(void * opaque_op)30 static void __dsc_dbg_op_timeout(void *opaque_op) 31 { 32 struct dsc_op *op = opaque_op; 33 34 qdf_print_thread_trace(op->thread); 35 QDF_DEBUG_PANIC("Operation '%s' exceeded %ums", 36 op->func, DSC_OP_TIMEOUT_MS); 37 } 38 39 /** 40 * __dsc_dbg_ops_init() - initialize debug ops data structures 41 * @ops: the ops container to initialize 42 * 43 * Return: None 44 */ __dsc_dbg_ops_init(struct dsc_ops * ops)45 static inline void __dsc_dbg_ops_init(struct dsc_ops *ops) 46 { 47 qdf_list_create(&ops->list, 0); 48 } 49 50 /** 51 * __dsc_dbg_ops_deinit() - de-initialize debug ops data structures 52 * @ops: the ops container to de-initialize 53 * 54 * Return: None 55 */ __dsc_dbg_ops_deinit(struct dsc_ops * ops)56 static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops) 57 { 58 qdf_list_destroy(&ops->list); 59 } 60 61 /** 62 * __dsc_dbg_ops_insert() - insert @func into the debug information in @ops 63 * @ops: the ops container to insert into 64 * @func: the debug information to insert 65 * 66 * Return: QDF_STATUS 67 */ __dsc_dbg_ops_insert(struct dsc_ops * ops,const char * func)68 static QDF_STATUS __dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func) 69 { 70 QDF_STATUS status; 71 struct dsc_op *op; 72 73 op = qdf_mem_malloc(sizeof(*op)); 74 if (!op) 75 return QDF_STATUS_E_NOMEM; 76 77 op->thread = qdf_get_current_task(); 78 status = qdf_timer_init(NULL, &op->timeout_timer, __dsc_dbg_op_timeout, 79 op, QDF_TIMER_TYPE_SW); 80 if (QDF_IS_STATUS_ERROR(status)) 81 goto free_op; 82 83 op->func = func; 84 85 qdf_timer_start(&op->timeout_timer, DSC_OP_TIMEOUT_MS); 86 qdf_list_insert_back(&ops->list, &op->node); 87 88 return QDF_STATUS_SUCCESS; 89 90 free_op: 91 qdf_mem_free(op); 92 93 return status; 94 } 95 96 /** 97 * __dsc_dbg_ops_remove() - remove @func from the debug information in @ops 98 * @ops: the ops container to remove from 99 * @func: the debug information to remove 100 * 101 * Return: None 102 */ __dsc_dbg_ops_remove(struct dsc_ops * ops,const char * func)103 static void __dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func) 104 { 105 struct dsc_op *op; 106 107 /* Global pending op depth is usually <=3. Use linear search for now */ 108 qdf_list_for_each(&ops->list, op, node) { 109 if (!qdf_str_eq(op->func, func)) 110 continue; 111 112 /* this is safe because we cease iteration */ 113 qdf_list_remove_node(&ops->list, &op->node); 114 115 qdf_timer_stop(&op->timeout_timer); 116 qdf_timer_free(&op->timeout_timer); 117 qdf_mem_free(op); 118 119 return; 120 } 121 122 QDF_DEBUG_PANIC("Driver op '%s' is not pending", func); 123 } 124 #else __dsc_dbg_ops_init(struct dsc_ops * ops)125 static inline void __dsc_dbg_ops_init(struct dsc_ops *ops) { } 126 __dsc_dbg_ops_deinit(struct dsc_ops * ops)127 static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops) { } 128 129 static inline QDF_STATUS __dsc_dbg_ops_insert(struct dsc_ops * ops,const char * func)130 __dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func) 131 { 132 return QDF_STATUS_SUCCESS; 133 } 134 135 static inline void __dsc_dbg_ops_remove(struct dsc_ops * ops,const char * func)136 __dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func) { } 137 #endif /* WLAN_DSC_DEBUG */ 138 __dsc_ops_init(struct dsc_ops * ops)139 void __dsc_ops_init(struct dsc_ops *ops) 140 { 141 ops->count = 0; 142 qdf_event_create(&ops->event); 143 __dsc_dbg_ops_init(ops); 144 } 145 __dsc_ops_deinit(struct dsc_ops * ops)146 void __dsc_ops_deinit(struct dsc_ops *ops) 147 { 148 /* assert no ops in flight */ 149 dsc_assert(!ops->count); 150 151 __dsc_dbg_ops_deinit(ops); 152 qdf_event_destroy(&ops->event); 153 } 154 __dsc_ops_insert(struct dsc_ops * ops,const char * func)155 QDF_STATUS __dsc_ops_insert(struct dsc_ops *ops, const char *func) 156 { 157 QDF_STATUS status; 158 159 status = __dsc_dbg_ops_insert(ops, func); 160 if (QDF_IS_STATUS_ERROR(status)) 161 return status; 162 163 ops->count++; 164 165 return QDF_STATUS_SUCCESS; 166 } 167 __dsc_ops_remove(struct dsc_ops * ops,const char * func)168 bool __dsc_ops_remove(struct dsc_ops *ops, const char *func) 169 { 170 dsc_assert(ops->count); 171 ops->count--; 172 173 __dsc_dbg_ops_remove(ops, func); 174 175 return ops->count == 0; 176 } 177 178 #ifdef WLAN_DSC_DEBUG __dsc_dbg_trans_timeout(void * opaque_trans)179 static void __dsc_dbg_trans_timeout(void *opaque_trans) 180 { 181 struct dsc_trans *trans = opaque_trans; 182 183 qdf_print_thread_trace(trans->thread); 184 185 if (cds_is_fw_down() && 186 !qdf_str_eq(trans->active_desc, "hdd_soc_recovery_shutdown")) 187 dsc_err("fw is down avoid panic"); 188 else 189 QDF_DEBUG_PANIC("Transition '%s' exceeded %ums", 190 trans->active_desc, DSC_TRANS_TIMEOUT_MS); 191 } 192 193 /** 194 * __dsc_dbg_trans_timeout_start() - start a timeout timer for @trans 195 * @trans: the active transition to start a timeout timer for 196 * 197 * Return: QDF_STATUS 198 */ __dsc_dbg_trans_timeout_start(struct dsc_trans * trans)199 static QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans) 200 { 201 QDF_STATUS status; 202 203 trans->thread = qdf_get_current_task(); 204 status = qdf_timer_init(NULL, &trans->timeout_timer, 205 __dsc_dbg_trans_timeout, trans, 206 QDF_TIMER_TYPE_SW); 207 if (QDF_IS_STATUS_ERROR(status)) 208 return status; 209 210 qdf_timer_start(&trans->timeout_timer, DSC_TRANS_TIMEOUT_MS); 211 212 return QDF_STATUS_SUCCESS; 213 } 214 215 /** 216 * __dsc_dbg_trans_timeout_stop() - stop the timeout timer for @trans 217 * @trans: the active transition to stop the timeout timer for 218 * 219 * Return: None 220 */ __dsc_dbg_trans_timeout_stop(struct dsc_trans * trans)221 static void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans) 222 { 223 qdf_timer_stop(&trans->timeout_timer); 224 qdf_timer_free(&trans->timeout_timer); 225 } 226 __dsc_dbg_tran_wait_timeout(void * opaque_tran)227 static void __dsc_dbg_tran_wait_timeout(void *opaque_tran) 228 { 229 struct dsc_tran *tran = opaque_tran; 230 231 qdf_print_thread_trace(tran->thread); 232 QDF_DEBUG_PANIC("Transition '%s' waited more than %ums", 233 tran->desc, DSC_TRANS_WAIT_TIMEOUT_MS); 234 } 235 236 /** 237 * __dsc_dbg_tran_wait_timeout_start() - start a timeout timer for @tran 238 * @tran: the pending transition to start a timeout timer for 239 * 240 * Return: QDF_STATUS 241 */ __dsc_dbg_tran_wait_timeout_start(struct dsc_tran * tran)242 static QDF_STATUS __dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran) 243 { 244 QDF_STATUS status; 245 246 tran->thread = qdf_get_current_task(); 247 status = qdf_timer_init(NULL, &tran->timeout_timer, 248 __dsc_dbg_tran_wait_timeout, tran, 249 QDF_TIMER_TYPE_SW); 250 if (QDF_IS_STATUS_ERROR(status)) 251 return status; 252 253 qdf_timer_start(&tran->timeout_timer, DSC_TRANS_WAIT_TIMEOUT_MS); 254 255 return QDF_STATUS_SUCCESS; 256 } 257 258 /** 259 * __dsc_dbg_tran_wait_timeout_stop() - stop the timeout timer for @tran 260 * @tran: the pending transition to stop the timeout timer for 261 * 262 * Return: None 263 */ __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran * tran)264 static void __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran *tran) 265 { 266 qdf_timer_stop(&tran->timeout_timer); 267 qdf_timer_free(&tran->timeout_timer); 268 } 269 #else __dsc_dbg_trans_timeout_start(struct dsc_trans * trans)270 static inline QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans) 271 { 272 return QDF_STATUS_SUCCESS; 273 } 274 __dsc_dbg_trans_timeout_stop(struct dsc_trans * trans)275 static inline void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans) { } 276 277 static inline QDF_STATUS __dsc_dbg_tran_wait_timeout_start(struct dsc_tran * tran)278 __dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran) 279 { 280 return QDF_STATUS_SUCCESS; 281 } 282 __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran * tran)283 static inline void __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran *tran) { } 284 #endif /* WLAN_DSC_DEBUG */ 285 __dsc_trans_init(struct dsc_trans * trans)286 void __dsc_trans_init(struct dsc_trans *trans) 287 { 288 trans->active_desc = NULL; 289 qdf_list_create(&trans->queue, 0); 290 } 291 __dsc_trans_deinit(struct dsc_trans * trans)292 void __dsc_trans_deinit(struct dsc_trans *trans) 293 { 294 qdf_list_destroy(&trans->queue); 295 trans->active_desc = NULL; 296 } 297 __dsc_trans_start(struct dsc_trans * trans,const char * desc)298 QDF_STATUS __dsc_trans_start(struct dsc_trans *trans, const char *desc) 299 { 300 QDF_STATUS status; 301 302 status = __dsc_dbg_trans_timeout_start(trans); 303 if (QDF_IS_STATUS_ERROR(status)) 304 return status; 305 306 dsc_assert(!trans->active_desc); 307 trans->active_desc = desc; 308 309 return QDF_STATUS_SUCCESS; 310 } 311 __dsc_trans_stop(struct dsc_trans * trans)312 void __dsc_trans_stop(struct dsc_trans *trans) 313 { 314 dsc_assert(trans->active_desc); 315 trans->active_desc = NULL; 316 __dsc_dbg_trans_timeout_stop(trans); 317 } 318 __dsc_trans_queue(struct dsc_trans * trans,struct dsc_tran * tran,const char * desc)319 QDF_STATUS __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran, 320 const char *desc) 321 { 322 QDF_STATUS status; 323 324 tran->abort = false; 325 tran->desc = desc; 326 qdf_event_create(&tran->event); 327 328 status = __dsc_dbg_tran_wait_timeout_start(tran); 329 if (QDF_IS_STATUS_ERROR(status)) 330 goto event_destroy; 331 332 qdf_list_insert_back(&trans->queue, &tran->node); 333 334 return QDF_STATUS_SUCCESS; 335 336 event_destroy: 337 qdf_event_destroy(&tran->event); 338 339 return status; 340 } 341 342 /** 343 * __dsc_trans_dequeue() - dequeue the next queued transition from @trans 344 * @trans: the transactions container to dequeue from 345 * 346 * Return: the dequeued transition, or NULL if @trans is empty 347 */ __dsc_trans_dequeue(struct dsc_trans * trans)348 static struct dsc_tran *__dsc_trans_dequeue(struct dsc_trans *trans) 349 { 350 QDF_STATUS status; 351 qdf_list_node_t *node; 352 struct dsc_tran *tran; 353 354 status = qdf_list_remove_front(&trans->queue, &node); 355 if (QDF_IS_STATUS_ERROR(status)) 356 return NULL; 357 358 tran = qdf_container_of(node, struct dsc_tran, node); 359 __dsc_dbg_tran_wait_timeout_stop(tran); 360 361 return tran; 362 } 363 __dsc_trans_abort(struct dsc_trans * trans)364 bool __dsc_trans_abort(struct dsc_trans *trans) 365 { 366 struct dsc_tran *tran; 367 368 tran = __dsc_trans_dequeue(trans); 369 if (!tran) 370 return false; 371 372 tran->abort = true; 373 qdf_event_set(&tran->event); 374 375 return true; 376 } 377 __dsc_trans_trigger(struct dsc_trans * trans)378 bool __dsc_trans_trigger(struct dsc_trans *trans) 379 { 380 struct dsc_tran *tran; 381 382 tran = __dsc_trans_dequeue(trans); 383 if (!tran) 384 return false; 385 386 __dsc_trans_start(trans, tran->desc); 387 qdf_event_set(&tran->event); 388 389 return true; 390 } 391 __dsc_trans_active(struct dsc_trans * trans)392 bool __dsc_trans_active(struct dsc_trans *trans) 393 { 394 return !!trans->active_desc; 395 } 396 __dsc_trans_queued(struct dsc_trans * trans)397 bool __dsc_trans_queued(struct dsc_trans *trans) 398 { 399 return !qdf_list_empty(&trans->queue); 400 } 401 __dsc_trans_active_or_queued(struct dsc_trans * trans)402 bool __dsc_trans_active_or_queued(struct dsc_trans *trans) 403 { 404 return __dsc_trans_active(trans) || __dsc_trans_queued(trans); 405 } 406 __dsc_tran_wait(struct dsc_tran * tran)407 QDF_STATUS __dsc_tran_wait(struct dsc_tran *tran) 408 { 409 QDF_STATUS status; 410 411 status = qdf_wait_single_event(&tran->event, 0); 412 qdf_event_destroy(&tran->event); 413 414 if (QDF_IS_STATUS_ERROR(status)) 415 return status; 416 417 if (tran->abort) 418 return QDF_STATUS_E_ABORTED; 419 420 return QDF_STATUS_SUCCESS; 421 } 422 423