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