qcacld-3.0: Add hdd_psoc_sync APIs

To bring HDD PSOC synchronization in line with recent VDEV enhancements,
add hdd_psoc_sync APIs. These allow for atomically looking up a psoc
synchronization context via device pointer, and protecting various call
flows via DSC primitives.

Change-Id: Iacc62454f0ca2c8f2098384f16d98713a4373361
CRs-Fixed: 2392089
This commit is contained in:
Dustin Brown 2019-01-31 15:39:58 -08:00 committed by nshrivas
parent 234ac6ca99
commit b7cad141e4
2 changed files with 469 additions and 16 deletions

View File

@ -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
*/

View File

@ -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;
}