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