diff --git a/core/hdd/inc/wlan_hdd_dsc.h b/core/hdd/inc/wlan_hdd_dsc.h index 3a4f96a932ee8..9d0f643789b9d 100644 --- a/core/hdd/inc/wlan_hdd_dsc.h +++ b/core/hdd/inc/wlan_hdd_dsc.h @@ -45,6 +45,162 @@ void hdd_dsc_deinit(void); */ struct dsc_psoc *hdd_dsc_psoc_from_wiphy(struct wiphy *wiphy); +/** + * struct hdd_psoc_sync - opaque synchronization handle for a psoc + */ +struct hdd_psoc_sync; + +/** + * hdd_psoc_sync_create() - create a psoc synchronization context + * @dsc_driver: parent dsc_driver to the psoc + * @out_psoc_sync: out parameter for the new synchronization context + * + * Return: Errno + */ +qdf_must_check int +hdd_psoc_sync_create(struct dsc_driver *dsc_driver, + struct hdd_psoc_sync **out_psoc_sync); + +/** + * hdd_psoc_sync_create_with_trans() - create a psoc synchronization context + * @dsc_driver: parent dsc_driver to the psoc + * @out_psoc_sync: out parameter for the new synchronization context + * + * For protecting the device creation process. + * + * Return: Errno + */ +#define hdd_psoc_sync_create_with_trans(dsc_driver, out_psoc_sync) \ + __hdd_psoc_sync_create_with_trans(dsc_driver, out_psoc_sync, __func__) + +qdf_must_check int +__hdd_psoc_sync_create_with_trans(struct dsc_driver *dsc_driver, + struct hdd_psoc_sync **out_psoc_sync, + const char *desc); + +/** + * hdd_psoc_sync_destroy() - destroy a psoc synchronization context + * @psoc_sync: the context to destroy + * + * Return: none + */ +void hdd_psoc_sync_destroy(struct hdd_psoc_sync *psoc_sync); + +/** + * hdd_psoc_sync_register() - register a psoc for operations/transitions + * @dev: the device to use as the operation/transition lookup key + * @psoc_sync: the psoc synchronization context to register + * + * Return: none + */ +void hdd_psoc_sync_register(struct device *dev, + struct hdd_psoc_sync *psoc_sync); + +/** + * hdd_psoc_sync_unregister() - unregister a psoc for operations/transitions + * @dev: the device originally used to register the psoc_sync context + * + * Return: the psoc synchronization context that was registered for @dev + */ +struct hdd_psoc_sync *hdd_psoc_sync_unregister(struct device *dev); + +/** + * hdd_psoc_sync_trans_start() - attempt to start a transition on @dev + * @dev: the device to transition + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +#define hdd_psoc_sync_trans_start(dev, out_psoc_sync) \ + __hdd_psoc_sync_trans_start(dev, out_psoc_sync, __func__) + +qdf_must_check int +__hdd_psoc_sync_trans_start(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync, + const char *desc); + +/** + * hdd_psoc_sync_trans_start_wait() - attempt to start a transition on @dev, + * blocking if a conflicting transition is in flight + * @dev: the device to transition + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +#define hdd_psoc_sync_trans_start_wait(dev, out_psoc_sync) \ + __hdd_psoc_sync_trans_start_wait(dev, out_psoc_sync, __func__) + +qdf_must_check int +__hdd_psoc_sync_trans_start_wait(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync, + const char *desc); + +/** + * hdd_psoc_sync_trans_resume() - resume a transition on @dev + * @dev: the device under transition + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +int hdd_psoc_sync_trans_resume(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync); + +/** + * hdd_psoc_sync_trans_stop() - stop a transition associated with @psoc_sync + * @psoc_sync: the synchonization context tracking the transition + * + * Return: none + */ +void hdd_psoc_sync_trans_stop(struct hdd_psoc_sync *psoc_sync); + +/** + * hdd_psoc_sync_assert_trans_protected() - assert that @dev is currently + * protected by a transition + * @dev: the device to check + * + * Return: none + */ +void hdd_psoc_sync_assert_trans_protected(struct device *dev); + +/** + * hdd_psoc_sync_op_start() - attempt to start an operation on @dev + * @dev: the device to operate against + * @out_psoc_sync: out parameter for the synchronization context registered with + * @dev, populated on success + * + * Return: Errno + */ +#define hdd_psoc_sync_op_start(dev, out_psoc_sync) \ + __hdd_psoc_sync_op_start(dev, out_psoc_sync, __func__) + +qdf_must_check int +__hdd_psoc_sync_op_start(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync, + const char *func); + +/** + * hdd_psoc_sync_op_stop() - stop an operation associated with @psoc_sync + * @psoc_sync: the synchonization context tracking the operation + * + * Return: none + */ +#define hdd_psoc_sync_op_stop(dev) \ + __hdd_psoc_sync_op_stop(dev, __func__) + +void __hdd_psoc_sync_op_stop(struct hdd_psoc_sync *psoc_sync, + const char *func); + +/** + * hdd_psoc_sync_wait_for_ops() - wait until all @psoc_sync operations complete + * @psoc_sync: the synchonization context tracking the operations + * + * Return: None + */ +void hdd_psoc_sync_wait_for_ops(struct hdd_psoc_sync *psoc_sync); + /** * struct hdd_vdev_sync - opaque synchronization handle for a vdev */ diff --git a/core/hdd/src/wlan_hdd_dsc.c b/core/hdd/src/wlan_hdd_dsc.c index 793e77910bdcf..e2f7ad5731d00 100644 --- a/core/hdd/src/wlan_hdd_dsc.c +++ b/core/hdd/src/wlan_hdd_dsc.c @@ -23,17 +23,292 @@ #include "wlan_dsc.h" #include "wlan_hdd_dsc.h" -struct dsc_psoc *hdd_dsc_psoc_from_wiphy(struct wiphy *wiphy) +/** + * struct hdd_psoc_sync - a psoc synchronization context + * @dev: the device used as a lookup key + * @dsc_psoc: the dsc_psoc used for synchronization + * @in_use: indicates if the context is being used + */ +struct hdd_psoc_sync { + struct device *dev; + struct dsc_psoc *dsc_psoc; + bool in_use; +}; + +#define WLAN_MAX_PSOCS 1 +static struct hdd_psoc_sync __hdd_psoc_sync_arr[WLAN_MAX_PSOCS]; +static qdf_spinlock_t __hdd_psoc_sync_lock; + +#define hdd_psoc_sync_lock_create() qdf_spinlock_create(&__hdd_psoc_sync_lock) +#define hdd_psoc_sync_lock_destroy() qdf_spinlock_destroy(&__hdd_psoc_sync_lock) +#define hdd_psoc_sync_lock() qdf_spin_lock_bh(&__hdd_psoc_sync_lock) +#define hdd_psoc_sync_unlock() qdf_spin_unlock_bh(&__hdd_psoc_sync_lock) +#define hdd_psoc_sync_lock_assert() \ + QDF_BUG(qdf_spin_is_locked(&__hdd_psoc_sync_lock)) + +static struct hdd_psoc_sync *hdd_psoc_sync_lookup(struct device *dev) { - struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + int i; - if (!hdd_ctx) + hdd_psoc_sync_lock_assert(); + + for (i = 0; i < QDF_ARRAY_SIZE(__hdd_psoc_sync_arr); i++) { + struct hdd_psoc_sync *psoc_sync = __hdd_psoc_sync_arr + i; + + if (psoc_sync->dev == dev) + return psoc_sync; + } + + return NULL; +} + +static struct hdd_psoc_sync *hdd_psoc_sync_get(void) +{ + int i; + + hdd_psoc_sync_lock_assert(); + + for (i = 0; i < QDF_ARRAY_SIZE(__hdd_psoc_sync_arr); i++) { + struct hdd_psoc_sync *psoc_sync = __hdd_psoc_sync_arr + i; + + if (!psoc_sync->in_use) { + psoc_sync->in_use = true; + return psoc_sync; + } + } + + return NULL; +} + +static void hdd_psoc_sync_put(struct hdd_psoc_sync *psoc_sync) +{ + hdd_psoc_sync_lock_assert(); + + qdf_mem_zero(psoc_sync, sizeof(*psoc_sync)); +} + +int hdd_psoc_sync_create(struct dsc_driver *dsc_driver, + struct hdd_psoc_sync **out_psoc_sync) +{ + QDF_STATUS status; + struct hdd_psoc_sync *psoc_sync; + + QDF_BUG(dsc_driver); + if (!dsc_driver) + return -EINVAL; + + QDF_BUG(out_psoc_sync); + if (!out_psoc_sync) + return -EINVAL; + + hdd_psoc_sync_lock(); + psoc_sync = hdd_psoc_sync_get(); + hdd_psoc_sync_unlock(); + if (!psoc_sync) + return -ENOMEM; + + status = dsc_psoc_create(dsc_driver, &psoc_sync->dsc_psoc); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_put; + + *out_psoc_sync = psoc_sync; + + return 0; + +sync_put: + hdd_psoc_sync_lock(); + hdd_psoc_sync_put(psoc_sync); + hdd_psoc_sync_unlock(); + + return qdf_status_to_os_return(status); +} + +int __hdd_psoc_sync_create_with_trans(struct dsc_driver *dsc_driver, + struct hdd_psoc_sync **out_psoc_sync, + const char *desc) +{ + struct hdd_psoc_sync *psoc_sync; + QDF_STATUS status; + int errno; + + errno = hdd_psoc_sync_create(dsc_driver, &psoc_sync); + if (errno) + return errno; + + status = dsc_psoc_trans_start(psoc_sync->dsc_psoc, desc); + if (QDF_IS_STATUS_ERROR(status)) + goto sync_destroy; + + *out_psoc_sync = psoc_sync; + + return 0; + +sync_destroy: + hdd_psoc_sync_destroy(psoc_sync); + + return qdf_status_to_os_return(status); +} + +void hdd_psoc_sync_destroy(struct hdd_psoc_sync *psoc_sync) +{ + QDF_BUG(psoc_sync); + if (!psoc_sync) + return; + + dsc_psoc_destroy(&psoc_sync->dsc_psoc); + + hdd_psoc_sync_lock(); + hdd_psoc_sync_put(psoc_sync); + hdd_psoc_sync_unlock(); +} + +void hdd_psoc_sync_register(struct device *dev, + struct hdd_psoc_sync *psoc_sync) +{ + QDF_BUG(dev); + QDF_BUG(psoc_sync); + if (!psoc_sync) + return; + + hdd_psoc_sync_lock(); + psoc_sync->dev = dev; + hdd_psoc_sync_unlock(); +} + +struct hdd_psoc_sync *hdd_psoc_sync_unregister(struct device *dev) +{ + struct hdd_psoc_sync *psoc_sync; + + QDF_BUG(dev); + if (!dev) return NULL; - if (!hdd_ctx->hdd_psoc) - return NULL; + hdd_psoc_sync_lock(); + psoc_sync = hdd_psoc_sync_lookup(dev); + if (psoc_sync) + psoc_sync->dev = NULL; + hdd_psoc_sync_unlock(); - return hdd_ctx->hdd_psoc->dsc_psoc; + return psoc_sync; +} + +typedef QDF_STATUS (*psoc_start_func)(struct dsc_psoc *, const char *); + +static int __hdd_psoc_sync_start_callback(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync, + const char *desc, + psoc_start_func psoc_start_cb) +{ + QDF_STATUS status; + struct hdd_psoc_sync *psoc_sync; + + hdd_psoc_sync_lock_assert(); + + *out_psoc_sync = NULL; + + psoc_sync = hdd_psoc_sync_lookup(dev); + if (!psoc_sync) + return -EAGAIN; + + status = psoc_start_cb(psoc_sync->dsc_psoc, desc); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + *out_psoc_sync = psoc_sync; + + return 0; +} + +int __hdd_psoc_sync_trans_start(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync, + const char *desc) +{ + int errno; + + hdd_psoc_sync_lock(); + errno = __hdd_psoc_sync_start_callback(dev, out_psoc_sync, desc, + dsc_psoc_trans_start); + hdd_psoc_sync_unlock(); + + return errno; +} + +int __hdd_psoc_sync_trans_start_wait(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync, + const char *desc) +{ + int errno; + + hdd_psoc_sync_lock(); + errno = __hdd_psoc_sync_start_callback(dev, out_psoc_sync, desc, + dsc_psoc_trans_start_wait); + hdd_psoc_sync_unlock(); + + return errno; +} + +static QDF_STATUS __assert_trans_cb(struct dsc_psoc *dsc_psoc, const char *desc) +{ + dsc_psoc_assert_trans_protected(dsc_psoc); + + return QDF_STATUS_SUCCESS; +} + +int hdd_psoc_sync_trans_resume(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync) +{ + int errno; + + hdd_psoc_sync_lock(); + errno = __hdd_psoc_sync_start_callback(dev, out_psoc_sync, NULL, + __assert_trans_cb); + hdd_psoc_sync_unlock(); + + return errno; +} + +void hdd_psoc_sync_trans_stop(struct hdd_psoc_sync *psoc_sync) +{ + dsc_psoc_trans_stop(psoc_sync->dsc_psoc); +} + +void hdd_psoc_sync_assert_trans_protected(struct device *dev) +{ + struct hdd_psoc_sync *psoc_sync; + + hdd_psoc_sync_lock(); + + psoc_sync = hdd_psoc_sync_lookup(dev); + QDF_BUG(psoc_sync); + if (psoc_sync) + dsc_psoc_assert_trans_protected(psoc_sync->dsc_psoc); + + hdd_psoc_sync_unlock(); +} + +int __hdd_psoc_sync_op_start(struct device *dev, + struct hdd_psoc_sync **out_psoc_sync, + const char *func) +{ + int errno; + + hdd_psoc_sync_lock(); + errno = __hdd_psoc_sync_start_callback(dev, out_psoc_sync, func, + _dsc_psoc_op_start); + hdd_psoc_sync_unlock(); + + return errno; +} + +void __hdd_psoc_sync_op_stop(struct hdd_psoc_sync *psoc_sync, + const char *func) +{ + _dsc_psoc_op_stop(psoc_sync->dsc_psoc, func); +} + +void hdd_psoc_sync_wait_for_ops(struct hdd_psoc_sync *psoc_sync) +{ + dsc_psoc_wait_for_ops(psoc_sync->dsc_psoc); } /** @@ -99,16 +374,6 @@ static void hdd_vdev_sync_put(struct hdd_vdev_sync *vdev_sync) qdf_mem_zero(vdev_sync, sizeof(*vdev_sync)); } -void hdd_dsc_init(void) -{ - hdd_vdev_sync_lock_create(); -} - -void hdd_dsc_deinit(void) -{ - hdd_vdev_sync_lock_destroy(); -} - int hdd_vdev_sync_create(struct wiphy *wiphy, struct hdd_vdev_sync **out_vdev_sync) { @@ -318,3 +583,35 @@ void hdd_vdev_sync_wait_for_ops(struct hdd_vdev_sync *vdev_sync) dsc_vdev_wait_for_ops(vdev_sync->dsc_vdev); } +void hdd_dsc_init(void) +{ + hdd_psoc_sync_lock_create(); + hdd_vdev_sync_lock_create(); +} + +void hdd_dsc_deinit(void) +{ + hdd_vdev_sync_lock_destroy(); + hdd_psoc_sync_lock_destroy(); +} + +struct dsc_psoc *hdd_dsc_psoc_from_wiphy(struct wiphy *wiphy) +{ + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct hdd_psoc_sync *psoc_sync; + struct dsc_psoc *dsc_psoc = NULL; + + if (!hdd_ctx) + return NULL; + + hdd_psoc_sync_lock(); + + psoc_sync = hdd_psoc_sync_lookup(hdd_ctx->parent_dev); + if (psoc_sync) + dsc_psoc = psoc_sync->dsc_psoc; + + hdd_psoc_sync_unlock(); + + return dsc_psoc; +} +