123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- /*
- * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- *
- * Permission to use, copy, modify, and/or distribute this software for
- * any purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
- #include "qdf_list.h"
- #include "qdf_mem.h"
- #include "qdf_status.h"
- #include "qdf_str.h"
- #include "qdf_threads.h"
- #include "qdf_timer.h"
- #include "__wlan_dsc.h"
- #include "cds_api.h"
- #ifdef WLAN_DSC_DEBUG
- static void __dsc_dbg_op_timeout(void *opaque_op)
- {
- struct dsc_op *op = opaque_op;
- qdf_print_thread_trace(op->thread);
- QDF_DEBUG_PANIC("Operation '%s' exceeded %ums",
- op->func, DSC_OP_TIMEOUT_MS);
- }
- /**
- * __dsc_dbg_ops_init() - initialize debug ops data structures
- * @ops: the ops container to initialize
- *
- * Return: None
- */
- static inline void __dsc_dbg_ops_init(struct dsc_ops *ops)
- {
- qdf_list_create(&ops->list, 0);
- }
- /**
- * __dsc_dbg_ops_deinit() - de-initialize debug ops data structures
- * @ops: the ops container to de-initialize
- *
- * Return: None
- */
- static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops)
- {
- qdf_list_destroy(&ops->list);
- }
- /**
- * __dsc_dbg_ops_insert() - insert @func into the debug information in @ops
- * @ops: the ops container to insert into
- * @func: the debug information to insert
- *
- * Return: QDF_STATUS
- */
- static QDF_STATUS __dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func)
- {
- QDF_STATUS status;
- struct dsc_op *op;
- op = qdf_mem_malloc(sizeof(*op));
- if (!op)
- return QDF_STATUS_E_NOMEM;
- op->thread = qdf_get_current_task();
- status = qdf_timer_init(NULL, &op->timeout_timer, __dsc_dbg_op_timeout,
- op, QDF_TIMER_TYPE_SW);
- if (QDF_IS_STATUS_ERROR(status))
- goto free_op;
- op->func = func;
- qdf_timer_start(&op->timeout_timer, DSC_OP_TIMEOUT_MS);
- qdf_list_insert_back(&ops->list, &op->node);
- return QDF_STATUS_SUCCESS;
- free_op:
- qdf_mem_free(op);
- return status;
- }
- /**
- * __dsc_dbg_ops_remove() - remove @func from the debug information in @ops
- * @ops: the ops container to remove from
- * @func: the debug information to remove
- *
- * Return: None
- */
- static void __dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func)
- {
- struct dsc_op *op;
- /* Global pending op depth is usually <=3. Use linear search for now */
- qdf_list_for_each(&ops->list, op, node) {
- if (!qdf_str_eq(op->func, func))
- continue;
- /* this is safe because we cease iteration */
- qdf_list_remove_node(&ops->list, &op->node);
- qdf_timer_stop(&op->timeout_timer);
- qdf_timer_free(&op->timeout_timer);
- qdf_mem_free(op);
- return;
- }
- QDF_DEBUG_PANIC("Driver op '%s' is not pending", func);
- }
- #else
- static inline void __dsc_dbg_ops_init(struct dsc_ops *ops) { }
- static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops) { }
- static inline QDF_STATUS
- __dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func)
- {
- return QDF_STATUS_SUCCESS;
- }
- static inline void
- __dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func) { }
- #endif /* WLAN_DSC_DEBUG */
- void __dsc_ops_init(struct dsc_ops *ops)
- {
- ops->count = 0;
- qdf_event_create(&ops->event);
- __dsc_dbg_ops_init(ops);
- }
- void __dsc_ops_deinit(struct dsc_ops *ops)
- {
- /* assert no ops in flight */
- dsc_assert(!ops->count);
- __dsc_dbg_ops_deinit(ops);
- qdf_event_destroy(&ops->event);
- }
- QDF_STATUS __dsc_ops_insert(struct dsc_ops *ops, const char *func)
- {
- QDF_STATUS status;
- status = __dsc_dbg_ops_insert(ops, func);
- if (QDF_IS_STATUS_ERROR(status))
- return status;
- ops->count++;
- return QDF_STATUS_SUCCESS;
- }
- bool __dsc_ops_remove(struct dsc_ops *ops, const char *func)
- {
- dsc_assert(ops->count);
- ops->count--;
- __dsc_dbg_ops_remove(ops, func);
- return ops->count == 0;
- }
- #ifdef WLAN_DSC_DEBUG
- static void __dsc_dbg_trans_timeout(void *opaque_trans)
- {
- struct dsc_trans *trans = opaque_trans;
- qdf_print_thread_trace(trans->thread);
- if (cds_is_fw_down())
- dsc_err("fw is down avoid panic");
- else
- QDF_DEBUG_PANIC("Transition '%s' exceeded %ums",
- trans->active_desc, DSC_TRANS_TIMEOUT_MS);
- }
- /**
- * __dsc_dbg_trans_timeout_start() - start a timeout timer for @trans
- * @trans: the active transition to start a timeout timer for
- *
- * Return: QDF_STATUS
- */
- static QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans)
- {
- QDF_STATUS status;
- trans->thread = qdf_get_current_task();
- status = qdf_timer_init(NULL, &trans->timeout_timer,
- __dsc_dbg_trans_timeout, trans,
- QDF_TIMER_TYPE_SW);
- if (QDF_IS_STATUS_ERROR(status))
- return status;
- qdf_timer_start(&trans->timeout_timer, DSC_TRANS_TIMEOUT_MS);
- return QDF_STATUS_SUCCESS;
- }
- /**
- * __dsc_dbg_trans_timeout_stop() - stop the timeout timer for @trans
- * @trans: the active transition to stop the timeout timer for
- *
- * Return: None
- */
- static void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans)
- {
- qdf_timer_stop(&trans->timeout_timer);
- qdf_timer_free(&trans->timeout_timer);
- }
- static void __dsc_dbg_tran_wait_timeout(void *opaque_tran)
- {
- struct dsc_tran *tran = opaque_tran;
- qdf_print_thread_trace(tran->thread);
- QDF_DEBUG_PANIC("Transition '%s' waited more than %ums",
- tran->desc, DSC_TRANS_WAIT_TIMEOUT_MS);
- }
- /**
- * __dsc_dbg_tran_wait_timeout_start() - start a timeout timer for @tran
- * @tran: the pending transition to start a timeout timer for
- *
- * Return: QDF_STATUS
- */
- static QDF_STATUS __dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran)
- {
- QDF_STATUS status;
- tran->thread = qdf_get_current_task();
- status = qdf_timer_init(NULL, &tran->timeout_timer,
- __dsc_dbg_tran_wait_timeout, tran,
- QDF_TIMER_TYPE_SW);
- if (QDF_IS_STATUS_ERROR(status))
- return status;
- qdf_timer_start(&tran->timeout_timer, DSC_TRANS_WAIT_TIMEOUT_MS);
- return QDF_STATUS_SUCCESS;
- }
- /**
- * __dsc_dbg_tran_wait_timeout_stop() - stop the timeout timer for @tran
- * @tran: the pending transition to stop the timeout timer for
- *
- * Return: None
- */
- static void __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran *tran)
- {
- qdf_timer_stop(&tran->timeout_timer);
- qdf_timer_free(&tran->timeout_timer);
- }
- #else
- static inline QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans)
- {
- return QDF_STATUS_SUCCESS;
- }
- static inline void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans) { }
- static inline QDF_STATUS
- __dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran)
- {
- return QDF_STATUS_SUCCESS;
- }
- static inline void __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran *tran) { }
- #endif /* WLAN_DSC_DEBUG */
- void __dsc_trans_init(struct dsc_trans *trans)
- {
- trans->active_desc = NULL;
- qdf_list_create(&trans->queue, 0);
- }
- void __dsc_trans_deinit(struct dsc_trans *trans)
- {
- qdf_list_destroy(&trans->queue);
- trans->active_desc = NULL;
- }
- QDF_STATUS __dsc_trans_start(struct dsc_trans *trans, const char *desc)
- {
- QDF_STATUS status;
- status = __dsc_dbg_trans_timeout_start(trans);
- if (QDF_IS_STATUS_ERROR(status))
- return status;
- dsc_assert(!trans->active_desc);
- trans->active_desc = desc;
- return QDF_STATUS_SUCCESS;
- }
- void __dsc_trans_stop(struct dsc_trans *trans)
- {
- dsc_assert(trans->active_desc);
- trans->active_desc = NULL;
- __dsc_dbg_trans_timeout_stop(trans);
- }
- QDF_STATUS __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran,
- const char *desc)
- {
- QDF_STATUS status;
- tran->abort = false;
- tran->desc = desc;
- qdf_event_create(&tran->event);
- status = __dsc_dbg_tran_wait_timeout_start(tran);
- if (QDF_IS_STATUS_ERROR(status))
- goto event_destroy;
- qdf_list_insert_back(&trans->queue, &tran->node);
- return QDF_STATUS_SUCCESS;
- event_destroy:
- qdf_event_destroy(&tran->event);
- return status;
- }
- /**
- * __dsc_trans_dequeue() - dequeue the next queued transition from @trans
- * @trans: the transactions container to dequeue from
- *
- * Return: the dequeued transition, or NULL if @trans is empty
- */
- static struct dsc_tran *__dsc_trans_dequeue(struct dsc_trans *trans)
- {
- QDF_STATUS status;
- qdf_list_node_t *node;
- struct dsc_tran *tran;
- status = qdf_list_remove_front(&trans->queue, &node);
- if (QDF_IS_STATUS_ERROR(status))
- return NULL;
- tran = qdf_container_of(node, struct dsc_tran, node);
- __dsc_dbg_tran_wait_timeout_stop(tran);
- return tran;
- }
- bool __dsc_trans_abort(struct dsc_trans *trans)
- {
- struct dsc_tran *tran;
- tran = __dsc_trans_dequeue(trans);
- if (!tran)
- return false;
- tran->abort = true;
- qdf_event_set(&tran->event);
- return true;
- }
- bool __dsc_trans_trigger(struct dsc_trans *trans)
- {
- struct dsc_tran *tran;
- tran = __dsc_trans_dequeue(trans);
- if (!tran)
- return false;
- __dsc_trans_start(trans, tran->desc);
- qdf_event_set(&tran->event);
- return true;
- }
- bool __dsc_trans_active(struct dsc_trans *trans)
- {
- return !!trans->active_desc;
- }
- bool __dsc_trans_queued(struct dsc_trans *trans)
- {
- return !qdf_list_empty(&trans->queue);
- }
- bool __dsc_trans_active_or_queued(struct dsc_trans *trans)
- {
- return __dsc_trans_active(trans) || __dsc_trans_queued(trans);
- }
- QDF_STATUS __dsc_tran_wait(struct dsc_tran *tran)
- {
- QDF_STATUS status;
- status = qdf_wait_single_event(&tran->event, 0);
- qdf_event_destroy(&tran->event);
- if (QDF_IS_STATUS_ERROR(status))
- return status;
- if (tran->abort)
- return QDF_STATUS_E_ABORTED;
- return QDF_STATUS_SUCCESS;
- }
|