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