From 7e39722b42a4977f75ede68e03eb395bbbcd1098 Mon Sep 17 00:00:00 2001 From: Dustin Brown Date: Fri, 6 Jul 2018 14:19:30 -0700 Subject: [PATCH] qcacld-3.0: Add Driver Synchronization Core (common) The Driver Synchronization Core (DSC) is a set of synchronization primitives for use by the driver's orchestration layer. It provides APIs for ensuring safe state transitions (including bring up and tear down) of major driver objects: a single driver, associated psocs, and their associated vdevs. APIs are divided into two categories: mutual exclusion of conflicting transitions, and operation tracking, blocking, and waiting capabilities. For part 1, add common infrastructure and headers. Change-Id: Id290e66d2dccd28b89fed5f285d3692ff3c814e7 CRs-Fixed: 2290260 --- dsc/inc/wlan_dsc.h | 55 +++++++ dsc/inc/wlan_dsc_driver.h | 141 ++++++++++++++++++ dsc/inc/wlan_dsc_psoc.h | 145 ++++++++++++++++++ dsc/inc/wlan_dsc_vdev.h | 143 ++++++++++++++++++ dsc/src/__wlan_dsc.c | 247 +++++++++++++++++++++++++++++++ dsc/src/__wlan_dsc.h | 304 ++++++++++++++++++++++++++++++++++++++ dsc/src/wlan_dsc_driver.c | 18 +++ dsc/src/wlan_dsc_psoc.c | 18 +++ dsc/src/wlan_dsc_vdev.c | 18 +++ dsc/test/wlan_dsc_test.c | 18 +++ dsc/test/wlan_dsc_test.h | 37 +++++ 11 files changed, 1144 insertions(+) create mode 100644 dsc/inc/wlan_dsc.h create mode 100644 dsc/inc/wlan_dsc_driver.h create mode 100644 dsc/inc/wlan_dsc_psoc.h create mode 100644 dsc/inc/wlan_dsc_vdev.h create mode 100644 dsc/src/__wlan_dsc.c create mode 100644 dsc/src/__wlan_dsc.h create mode 100644 dsc/src/wlan_dsc_driver.c create mode 100644 dsc/src/wlan_dsc_psoc.c create mode 100644 dsc/src/wlan_dsc_vdev.c create mode 100644 dsc/test/wlan_dsc_test.c create mode 100644 dsc/test/wlan_dsc_test.h diff --git a/dsc/inc/wlan_dsc.h b/dsc/inc/wlan_dsc.h new file mode 100644 index 0000000000..49dd2ddfe2 --- /dev/null +++ b/dsc/inc/wlan_dsc.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 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. + */ + +/** + * DOC: Driver Synchronization Core (DSC) APIs for use by the driver + * orchestration layer. + * + * This infrastructure accomplishes two high level goals: + * 1) Replace ad-hoc locking/flags (hdd_init_deinit_lock, + * iface_change_lock, con_mode_flag, etc., etc., etc.) + * 2) Make cds_ssr_protect() and driver state checking atomic + * + * These two goals are commplished in DSC via two corollary concepts: + * 1) Transitions (as in driver state machine transitions) + * These are mutually exclusive, and replace ad-hoc locking + * 2) Operations (as in operations the driver is currently servicing) + * These execute concurrently with other operations, and replace + * cds_ssr_protect(). Any active transition causes new operations to be + * rejected, in the same way as cds_ssr_protect/hdd_validate_context would. + * + * Transitions and operations are split into 3 distinct levels: driver, psoc, + * and vdev. These levels are arranged into a tree, with a single driver at + * the root, zero or more psocs per driver, and zero or more vdevs per psoc. + * + * High level transitions block transitions and operations at the same level + * down, and low level transitions block transitions at the same level up. So a + * driver transition effectively prevents any new activity in the system, while + * a vdev transition prevents transtitions on the same vdev, its parent psoc, + * and the driver. This also means that sibling nodes can transition at the same + * time, e.g. one vdev going up at the same time another is going down. + */ + +#ifndef __WLAN_DSC_H +#define __WLAN_DSC_H + +#include "wlan_dsc_driver.h" +#include "wlan_dsc_psoc.h" +#include "wlan_dsc_vdev.h" + +#endif /* __WLAN_DSC_H */ diff --git a/dsc/inc/wlan_dsc_driver.h b/dsc/inc/wlan_dsc_driver.h new file mode 100644 index 0000000000..88c73d4730 --- /dev/null +++ b/dsc/inc/wlan_dsc_driver.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018 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. + */ + +/** + * DOC: Driver Synchronization Core (DSC) driver-level APIs + */ + +#ifndef __WLAN_DSC_DRIVER_H +#define __WLAN_DSC_DRIVER_H + +#include "qdf_status.h" + +/** + * struct dsc_driver - opaque dsc driver context + */ +struct dsc_driver; + +/** + * dsc_driver_create() - create a dsc driver context + * @out_driver: opaque double pointer to assign the new context to + * + * Return: QDF_STATUS + */ +QDF_STATUS dsc_driver_create(struct dsc_driver **out_driver); + +/** + * dsc_driver_destroy() - destroy a dsc driver context + * @out_driver: opaque double pointer to context to destroy and NULL + * + * Note, this: + * - aborts all queued transitions on @driver + * - asserts @driver has no attached psoc's + * - asserts @driver has no operations in flight + * + * Return: None + */ +void dsc_driver_destroy(struct dsc_driver **out_driver); + +/** + * dsc_driver_trans_start() - start a transition on @driver + * @driver: the driver to start a transition on + * @desc: a unique description of the transition to start + * + * This API immediately aborts if a transition on @driver is already in flight + * + * Call dsc_driver_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already in flight + */ +QDF_STATUS dsc_driver_trans_start(struct dsc_driver *driver, const char *desc); + +/** + * dsc_driver_trans_start_wait() - start a transition on @driver, blocking if a + * transition is already in flight + * @driver: the driver to start a transition on + * @desc: a unique description of the transition to start + * + * Call dsc_driver_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight + */ +QDF_STATUS +dsc_driver_trans_start_wait(struct dsc_driver *driver, const char *desc); + +/** + * dsc_driver_trans_stop() - complete current transition in flight on @driver + * @driver: the driver to complete the transition on + * + * Note: this asserts a transition is currently in flight on @driver + * + * Return: None + */ +void dsc_driver_trans_stop(struct dsc_driver *driver); + +/** + * dsc_driver_trans_assert() - assert transition in flight on @driver + * @driver: the driver to assert transition in flight on + * + * Return: None + */ +void dsc_driver_trans_assert(struct dsc_driver *driver); + +/** + * dsc_driver_op_start() - start an operation on @driver + * @driver: the driver to start an operation on + * + * Return: + * QDF_STATUS_SUCCESS - operation started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - operation cannot currently be started + * QDF_STATUS_E_NOMEM - out of memory + */ +#define dsc_driver_op_start(driver) _dsc_driver_op_start(driver, __func__) +QDF_STATUS _dsc_driver_op_start(struct dsc_driver *driver, const char *func); + +/** + * dsc_driver_op_stop() - complete operation with matching @func on @driver + * @driver: the driver to stop an operation on + * + * Note: this asserts @func was previously started + * + * Return: None + */ +#define dsc_driver_op_stop(driver) _dsc_driver_op_stop(driver, __func__) +void _dsc_driver_op_stop(struct dsc_driver *driver, const char *func); + +/** + * dsc_driver_wait_for_ops() - blocks until all operations on @driver have + * stopped + * @driver: the driver to wait for operations on + * + * Note: this asserts that @driver cannot currently transition + * + * Return: None + */ +void dsc_driver_wait_for_ops(struct dsc_driver *driver); + +#endif /* __WLAN_DSC_DRIVER_H */ diff --git a/dsc/inc/wlan_dsc_psoc.h b/dsc/inc/wlan_dsc_psoc.h new file mode 100644 index 0000000000..050f5f68ad --- /dev/null +++ b/dsc/inc/wlan_dsc_psoc.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 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. + */ + +/** + * DOC: Driver Synchronization Core (DSC) psoc-level APIs + */ + +#ifndef __WLAN_DSC_PSOC_H +#define __WLAN_DSC_PSOC_H + +#include "qdf_status.h" + +/** + * struct dsc_psoc - opaque dsc psoc context + */ +struct dsc_psoc; + +/** + * dsc_psoc_create() - create a dsc psoc context + * @driver: parent dsc driver context + * @out_psoc: opaque double pointer to assign the new context to + * + * Note: this attaches @out_psoc to @driver + * + * Return: QDF_STATUS + */ +QDF_STATUS +dsc_psoc_create(struct dsc_driver *driver, struct dsc_psoc **out_psoc); + +/** + * dsc_psoc_destroy() - destroy a dsc psoc context + * @out_psoc: opaque double pointer to context to destroy and NULL + * + * Note, this: + * - detaches @out_psoc from its parent driver context + * - aborts all queued transitions on @psoc + * - asserts @psoc has no attached vdev's + * - asserts @psoc has no operations in flight + * + * Return: None + */ +void dsc_psoc_destroy(struct dsc_psoc **out_psoc); + +/** + * dsc_psoc_trans_start() - start a transition on @psoc + * @psoc: the psoc to start a transition on + * @desc: a unique description of the transition to start + * + * This API immediately aborts if a transition on @psoc is already in flight + * + * Call dsc_psoc_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already in flight + */ +QDF_STATUS dsc_psoc_trans_start(struct dsc_psoc *psoc, const char *desc); + +/** + * dsc_psoc_trans_start_wait() - start a transition on @psoc, blocking if a + * transition is already in flight + * @psoc: the psoc to start a transition on + * @desc: a unique description of the transition to start + * + * Call dsc_psoc_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight + * QDF_STATUS_E_ABORTED - transition was aborted + */ +QDF_STATUS dsc_psoc_trans_start_wait(struct dsc_psoc *psoc, const char *desc); + +/** + * dsc_psoc_trans_stop() - complete current transition in flight on @psoc + * @psoc: the psoc to complete the transition on + * + * Note: this asserts a transition is currently in flight on @psoc + * + * Return: None + */ +void dsc_psoc_trans_stop(struct dsc_psoc *psoc); + +/** + * dsc_psoc_trans_assert() - assert transition in flight on @psoc + * @psoc: the psoc to assert transition in flight on + * + * Return: None + */ +void dsc_psoc_trans_assert(struct dsc_psoc *psoc); + +/** + * dsc_psoc_op_start() - start an operation on @psoc + * @psoc: the psoc to start an operation on + * + * Return: + * QDF_STATUS_SUCCESS - operation started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - operation cannot currently be started + * QDF_STATUS_E_NOMEM - out of memory + */ +#define dsc_psoc_op_start(psoc) _dsc_psoc_op_start(psoc, __func__) +QDF_STATUS _dsc_psoc_op_start(struct dsc_psoc *psoc, const char *func); + +/** + * dsc_psoc_op_stop() - complete operation with matching @func on @psoc + * @psoc: the psoc to stop an operation on + * + * Note: this asserts @func was previously started + * + * Return: None + */ +#define dsc_psoc_op_stop(psoc) _dsc_psoc_op_stop(psoc, __func__) +void _dsc_psoc_op_stop(struct dsc_psoc *psoc, const char *func); + +/** + * dsc_psoc_wait_for_ops() - blocks until all operations on @psoc have stopped + * @psoc: the psoc to wait for operations on + * + * Note: this asserts that @psoc cannot currently transition + * + * Return: None + */ +void dsc_psoc_wait_for_ops(struct dsc_psoc *psoc); + +#endif /* __WLAN_DSC_PSOC_H */ diff --git a/dsc/inc/wlan_dsc_vdev.h b/dsc/inc/wlan_dsc_vdev.h new file mode 100644 index 0000000000..12710111ad --- /dev/null +++ b/dsc/inc/wlan_dsc_vdev.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018 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. + */ + +/** + * DOC: Driver Synchronization Core (DSC) vdev-level APIs + */ + +#ifndef __WLAN_DSC_VDEV_H +#define __WLAN_DSC_VDEV_H + +#include "qdf_status.h" + +/** + * struct dsc_vdev - opaque dsc vdev context + */ +struct dsc_vdev; + +/** + * dsc_vdev_create() - create a dsc vdev context + * @psoc: parent dsc psoc context + * @out_vdev: opaque double pointer to assign the new context to + * + * Note: this attaches @out_vdev to @psoc + * + * Return: QDF_STATUS + */ +QDF_STATUS dsc_vdev_create(struct dsc_psoc *psoc, struct dsc_vdev **out_vdev); + +/** + * dsc_vdev_destroy() - destroy a dsc vdev context + * @out_vdev: opaque double pointer to context to destroy and NULL + * + * Note, this: + * - detaches @out_vdev from its parent psoc context + * - aborts all queued transitions on @vdev + * - asserts @vdev has no operations in flight + * + * Return: None + */ +void dsc_vdev_destroy(struct dsc_vdev **out_vdev); + +/** + * dsc_vdev_trans_start() - start a transition on @vdev + * @vdev: the vdev to start a transition on + * @desc: a unique description of the transition to start + * + * This API immediately aborts if a transition on @vdev is already in flight + * + * Call dsc_vdev_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already in flight + */ +QDF_STATUS dsc_vdev_trans_start(struct dsc_vdev *vdev, const char *desc); + +/** + * dsc_vdev_trans_start_wait() - start a transition on @vdev, blocking if a + * transition is already in flight + * @vdev: the vdev to start a transition on + * @desc: a unique description of the transition to start + * + * Call dsc_vdev_trans_stop() to complete the transition. + * + * Return: + * QDF_STATUS_SUCCESS - transition started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - transition cannot currently be started + * QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight + * QDF_STATUS_E_ABORTED - transition was aborted + */ +QDF_STATUS dsc_vdev_trans_start_wait(struct dsc_vdev *vdev, const char *desc); + +/** + * dsc_vdev_trans_stop() - complete current transition in flight on @vdev + * @vdev: the vdev to complete the transition on + * + * Note: this asserts a transition is currently in flight on @vdev + * + * Return: None + */ +void dsc_vdev_trans_stop(struct dsc_vdev *vdev); + +/** + * dsc_vdev_trans_assert() - assert transition in flight on @vdev + * @vdev: the vdev to assert transition in flight on + * + * Return: None + */ +void dsc_vdev_trans_assert(struct dsc_vdev *vdev); + +/** + * dsc_vdev_op_start() - start an operation on @vdev + * @vdev: the vdev to start an operation on + * + * Return: + * QDF_STATUS_SUCCESS - operation started succcessfully + * QDF_STATUS_E_INVAL - invalid request (causes debug panic) + * QDF_STATUS_E_AGAIN - operation cannot currently be started + * QDF_STATUS_E_NOMEM - out of memory + */ +#define dsc_vdev_op_start(vdev) _dsc_vdev_op_start(vdev, __func__) +QDF_STATUS _dsc_vdev_op_start(struct dsc_vdev *vdev, const char *func); + +/** + * dsc_vdev_op_stop() - complete operation with matching @func on @vdev + * @vdev: the vdev to stop an operation on + * + * Note: this asserts @func was previously started + * + * Return: None + */ +#define dsc_vdev_op_stop(vdev) _dsc_vdev_op_stop(vdev, __func__) +void _dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func); + +/** + * dsc_vdev_wait_for_ops() - blocks until all operations on @vdev have stopped + * @vdev: the vdev to wait for operations on + * + * Note: this asserts that @vdev cannot currently transition + * + * Return: None + */ +void dsc_vdev_wait_for_ops(struct dsc_vdev *vdev); + +#endif /* __WLAN_DSC_VDEV_H */ diff --git a/dsc/src/__wlan_dsc.c b/dsc/src/__wlan_dsc.c new file mode 100644 index 0000000000..25aad7846a --- /dev/null +++ b/dsc/src/__wlan_dsc.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2018 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 "__wlan_dsc.h" + +#ifdef WLAN_DSC_DEBUG +/** + * __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) +{ + struct dsc_op *op; + + op = qdf_mem_malloc(sizeof(*op)); + if (!op) + return QDF_STATUS_E_NOMEM; + + op->func = func; + + qdf_list_insert_back(&ops->list, &op->node); + + return QDF_STATUS_SUCCESS; +} + +/** + * __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_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; +} + +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; +} + +void __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran, + const char *desc) +{ + tran->abort = false; + tran->desc = desc; + qdf_event_create(&tran->event); + qdf_list_insert_back(&trans->queue, &tran->node); +} + +/** + * __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; + + status = qdf_list_remove_front(&trans->queue, &node); + if (QDF_IS_STATUS_ERROR(status)) + return NULL; + + return qdf_container_of(node, struct dsc_tran, node); +} + +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; + + trans->active_desc = 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, DSC_TRANS_TIMEOUT); + 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; +} + diff --git a/dsc/src/__wlan_dsc.h b/dsc/src/__wlan_dsc.h new file mode 100644 index 0000000000..a7b5b3cc23 --- /dev/null +++ b/dsc/src/__wlan_dsc.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2018 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. + */ + +/** + * DOC: Driver State Management (DSC) APIs for *internal* use + */ + +#ifndef ____WLAN_DSC_H +#define ____WLAN_DSC_H + +#include "qdf_event.h" +#include "qdf_list.h" +#include "qdf_trace.h" +#include "qdf_types.h" +#include "wlan_dsc.h" + +#define dsc_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_QDF, params) +#define dsc_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_QDF, params) +#define dsc_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_QDF, params) + +#define dsc_enter_exit dsc_debug +#define dsc_enter() dsc_enter_exit("enter") +#define dsc_enter_str(str) dsc_enter_exit("enter(\"%s\")", str) +#define dsc_exit() dsc_enter_exit("exit") +#define dsc_exit_status(status) dsc_enter_exit("exit(status:%u)", status) + +static inline bool __dsc_assert(const bool cond, const char *cond_str, + const char *func, const uint32_t line) +{ + if (cond) + return true; + + QDF_DEBUG_PANIC_FL(func, line, "Failed assertion '%s'!", cond_str); + + return false; +} + +#define dsc_assert(cond) __dsc_assert(cond, #cond, __func__, __LINE__) +#define dsc_assert_success(status) dsc_assert(QDF_IS_STATUS_SUCCESS(status)) + +#ifdef WLAN_DSC_DEBUG +#define DSC_TRANS_TIMEOUT 120000 /* 2 minutes */ +#else +#define DSC_TRANS_TIMEOUT 0 /* no timeout */ +#endif + +#ifdef WLAN_DSC_DEBUG +/** + * struct dsc_op - list node for operation tracking information + * @node: list node + * @func: name of the function the operation was started from + */ +struct dsc_op { + qdf_list_node_t node; + const char *func; +}; +#endif /* WLAN_DSC_DEBUG */ + +/** + * struct dsc_ops - operations in flight tracking container + * @list: list for tracking debug information + * @count: count of current operations in flight + * @event: event used to wait in *_wait_for_ops() APIs + */ +struct dsc_ops { +#ifdef WLAN_DSC_DEBUG + qdf_list_t list; +#endif + uint32_t count; + qdf_event_t event; +}; + +/** + * struct dsc_pending_trans - representation of a pending transition + * @abort: used to indicate if the transition stopped waiting due to an abort + * @desc: unique description of the transition + * @node: list node + * @event: event used to wait in *_start_trans_wait() APIs + */ +struct dsc_tran { + bool abort; + const char *desc; + qdf_list_node_t node; + qdf_event_t event; +}; + +/** + * struct dsc_trans - transition information container + * @active_desc: unique description of the current transition in progress + * @queue: queue of pending transitions + */ +struct dsc_trans { + const char *active_desc; + qdf_list_t queue; +}; + +/** + * struct dsc_driver - concrete dsc driver context + * @lock: lock under which all dsc APIs execute + * @psocs: list of children psoc contexts + * @trans: transition tracking container for this node + * @ops: operations in flight tracking container for this node + */ +struct dsc_driver { + struct qdf_spinlock lock; + qdf_list_t psocs; + struct dsc_trans trans; + struct dsc_ops ops; +}; + +/** + * struct dsc_psoc - concrete dsc psoc context + * @node: list node for membership in @driver->psocs + * @driver: parent driver context + * @vdevs: list of children vdevs contexts + * @trans: transition tracking container for this node + * @ops: operations in flight tracking container for this node + */ +struct dsc_psoc { + qdf_list_node_t node; + struct dsc_driver *driver; + qdf_list_t vdevs; + struct dsc_trans trans; + struct dsc_ops ops; +}; + +/** + * struct dsc_vdev - concrete dsc vdev context + * @node: list node for membership in @psoc->vdevs + * @psoc: parent psoc context + * @trans: transition tracking container for this node + * @ops: operations in flight tracking container for this node + */ +struct dsc_vdev { + qdf_list_node_t node; + struct dsc_psoc *psoc; + struct dsc_trans trans; + struct dsc_ops ops; +}; + +#define dsc_for_each_driver_psoc(driver_ptr, psoc_cursor) \ + qdf_list_for_each(&(driver_ptr)->psocs, psoc_cursor, node) + +#define dsc_for_each_psoc_vdev(psoc_ptr, vdev_cursor) \ + qdf_list_for_each(&(psoc_ptr)->vdevs, vdev_cursor, node) + +/** + * __dsc_lock() - grab the dsc global lock + * + * Return: None + */ +void __dsc_lock(void); + +/** + * __dsc_unlock() - release the dsc global lock + * + * Return: None + */ +void __dsc_unlock(void); + +/** + * __dsc_ops_init() - initialize @ops + * @ops: the ops container to initialize + * + * Return: None + */ +void __dsc_ops_init(struct dsc_ops *ops); + +/** + * __dsc_ops_deinit() - de-initialize @ops + * @ops: the ops container to de-initialize + * + * Return: None + */ +void __dsc_ops_deinit(struct dsc_ops *ops); + +/** + * __dsc_ops_insert() - insert @func into the trakcing information in @ops + * @ops: the ops container to insert into + * @func: the debug information to insert + * + * Return: QDF_STATUS + */ +QDF_STATUS __dsc_ops_insert(struct dsc_ops *ops, const char *func); + +/** + * __dsc_ops_remove() - remove @func from the tracking information in @ops + * @ops: the ops container to remove from + * @func: the debug information to remove + * + * Return: None + */ +bool __dsc_ops_remove(struct dsc_ops *ops, const char *func); + +/** + * __dsc_trans_init() - initialize @trans + * @trans: the trans container to initialize + * + * Return: None + */ +void __dsc_trans_init(struct dsc_trans *trans); + +/** + * __dsc_trans_deinit() - de-initialize @trans + * @trans: the trans container to de-initialize + * + * Return: None + */ +void __dsc_trans_deinit(struct dsc_trans *trans); + +/** + * __dsc_trans_queue() - queue @tran at the back of @trans + * @trans: the transitions container to enqueue to + * @tran: the transition to enqueue + * @desc: unique description of the transition being queued + * + * Return: None + */ +void __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran, + const char *desc); + +/** + * __dsc_tran_wait() - block until @tran completes + * @tran: the transition to wait on + * + * Return: QDF_STATUS + */ +QDF_STATUS __dsc_tran_wait(struct dsc_tran *tran); + +/** + * __dsc_trans_abort() - abort the next queued transition from @trans + * @trans: the transitions container to abort from + * + * Return: true if a transition was aborted, false if @trans is empty + */ +bool __dsc_trans_abort(struct dsc_trans *trans); + +/** + * __dsc_trans_trigger() - trigger the next queued trans in @trans + * @trans: the transitions container to trigger from + * + * Return: true if a transition was triggered + */ +bool __dsc_trans_trigger(struct dsc_trans *trans); + +/** + * __dsc_trans_active() - check if a transition is active in @trans + * @trans: the transitions container to check + * + * Return: true if @trans has an active transition + */ +bool __dsc_trans_active(struct dsc_trans *trans); + +/** + * __dsc_trans_queued() - check if a transition is queued in @trans + * @trans: the transitions container to check + * + * Return: true if @trans has a queued transition + */ +bool __dsc_trans_queued(struct dsc_trans *trans); + +/** + * __dsc_trans_active_or_queued() - check if a transition is active or queued + * in @trans + * @trans: the transitions container to check + * + * Return: true if @trans has an active or queued transition + */ +bool __dsc_trans_active_or_queued(struct dsc_trans *trans); + +/** + * __dsc_driver_trans_trigger_checked() - trigger any next pending driver + * transition, only after passing the "can trans" check + * + * Return: true if the trigger was "handled." This indicates down-tree nodes + * should _not_ attempt to trigger a new transition. + */ +bool __dsc_driver_trans_trigger_checked(struct dsc_driver *driver); + +/** + * __dsc_psoc_trans_trigger_checked() - trigger any next pending psoc + * transition, only after passing the "can trans" check + * + * Return: true if the trigger was "handled." This indicates down-tree nodes + * should _not_ attempt to trigger a new transition. + */ +bool __dsc_psoc_trans_trigger_checked(struct dsc_psoc *psoc); + +#endif /* ____WLAN_DSC_H */ diff --git a/dsc/src/wlan_dsc_driver.c b/dsc/src/wlan_dsc_driver.c new file mode 100644 index 0000000000..7bbea0cc11 --- /dev/null +++ b/dsc/src/wlan_dsc_driver.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018 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. + */ + diff --git a/dsc/src/wlan_dsc_psoc.c b/dsc/src/wlan_dsc_psoc.c new file mode 100644 index 0000000000..7bbea0cc11 --- /dev/null +++ b/dsc/src/wlan_dsc_psoc.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018 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. + */ + diff --git a/dsc/src/wlan_dsc_vdev.c b/dsc/src/wlan_dsc_vdev.c new file mode 100644 index 0000000000..7bbea0cc11 --- /dev/null +++ b/dsc/src/wlan_dsc_vdev.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018 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. + */ + diff --git a/dsc/test/wlan_dsc_test.c b/dsc/test/wlan_dsc_test.c new file mode 100644 index 0000000000..7bbea0cc11 --- /dev/null +++ b/dsc/test/wlan_dsc_test.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018 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. + */ + diff --git a/dsc/test/wlan_dsc_test.h b/dsc/test/wlan_dsc_test.h new file mode 100644 index 0000000000..b2c23d2ace --- /dev/null +++ b/dsc/test/wlan_dsc_test.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef __WLAN_DSC_TEST +#define __WLAN_DSC_TEST + +#ifdef WLAN_DSC_TEST +/** + * dsc_unit_test() - run the dsc unit test suite + * + * Return: number of failed test cases + */ +uint32_t dsc_unit_test(void); +#else +static inline uint32_t dsc_unit_test(void) +{ + return 0; +} +#endif /* WLAN_DSC_TEST */ + +#endif /* __WLAN_DSC_TEST */ +