Browse Source

qcacld-3.0: Change hdd_vdev APIs into hdd_vdev_sync

The hdd_vdev APIs recently picked up the capability to atomically lookup
a dsc_vdev by net_device pointer key and start an operation. This lookup
mechanism is also useful for controlling transitions, so extend the
concept there as well. Additionally, add create and destroy APIs to
allow for keeping all of the DSC abstraction in one place. Finally,
rename the hdd_vdev APIs to hdd_vdev_sync to better convey their intent.

Change-Id: Ic4b6c6991b1ddce5afa54ab2207628c864bc240b
CRs-Fixed: 2385667
Dustin Brown 6 years ago
parent
commit
4738d472f0
2 changed files with 342 additions and 97 deletions
  1. 121 29
      core/hdd/inc/wlan_hdd_dsc.h
  2. 221 68
      core/hdd/src/wlan_hdd_dsc.c

+ 121 - 29
core/hdd/inc/wlan_hdd_dsc.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ * 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
@@ -19,6 +19,7 @@
 #ifndef __WLAN_HDD_DSC_H__
 #define __WLAN_HDD_DSC_H__
 
+#include "qdf_types.h"
 #include "wlan_dsc.h"
 #include "wlan_hdd_main.h"
 
@@ -45,56 +46,147 @@ void hdd_dsc_deinit(void);
 struct dsc_psoc *hdd_dsc_psoc_from_wiphy(struct wiphy *wiphy);
 
 /**
- * hdd_vdev_ops_register() - register a dsc_vdev to accept new operations
- * @net_dev: the net_device which will be used for lookup during op start
- * @dsc_vdev: the dsc_vdev to use to protect operations on @net_dev
+ * struct hdd_vdev_sync - opaque synchronization handle for a vdev
+ */
+struct hdd_vdev_sync;
+
+/**
+ * hdd_vdev_sync_create() - create a vdev synchronization context
+ * @wiphy: parent wiphy to the vdev
+ * @out_vdev_sync: out parameter for the new synchronization context
  *
- * Return: None
+ * Return: Errno
  */
-void hdd_vdev_ops_register(struct net_device *net_dev,
-			   struct dsc_vdev *dsc_vdev);
+qdf_must_check int
+hdd_vdev_sync_create(struct wiphy *wiphy, struct hdd_vdev_sync **out_vdev_sync);
 
 /**
- * hdd_vdev_ops_unregister() - unregister a dsc_vdev to reject future operations
- * @net_dev: the net_device to use for lookup
+ * hdd_vdev_sync_create_with_trans() - create a vdev synchronization context
+ * @wiphy: parent wiphy to the vdev
+ * @out_vdev_sync: out parameter for the new synchronization context
  *
- * Return: None
+ * For protecting the net_device creation process.
+ *
+ * Return: Errno
+ */
+#define hdd_vdev_sync_create_with_trans(wiphy, out_vdev_sync) \
+	__hdd_vdev_sync_create_with_trans(wiphy, out_vdev_sync, __func__)
+
+qdf_must_check int
+__hdd_vdev_sync_create_with_trans(struct wiphy *wiphy,
+				  struct hdd_vdev_sync **out_vdev_sync,
+				  const char *desc);
+
+/**
+ * hdd_vdev_sync_destroy() - destroy a vdev synchronization context
+ * @vdev_sync: the context to destroy
+ *
+ * Return: none
  */
-void hdd_vdev_ops_unregister(struct net_device *net_dev);
+void hdd_vdev_sync_destroy(struct hdd_vdev_sync *vdev_sync);
 
 /**
- * struct hdd_vdev_op - opaque handle used to identify a specific vdev operation
+ * hdd_vdev_sync_register() - register a vdev for operations/transitions
+ * @net_dev: the net_device to use as the operation/transition lookup key
+ * @vdev_sync: the vdev synchronization context to register
+ *
+ * Return: none
  */
-struct hdd_vdev_op;
+void hdd_vdev_sync_register(struct net_device *net_dev,
+			    struct hdd_vdev_sync *vdev_sync);
 
 /**
- * hdd_vdev_op_start() - attempt to start a vdev-level driver operation
- * @net_dev: the net_device to start the operation on
+ * hdd_vdev_sync_unregister() - unregister a vdev for operations/transitions
+ * @net_dev: the net_device originally used to register the vdev_sync context
  *
- * Return: an operation handle on success, NULL on failure
+ * Return: the vdev synchronization context that was registered for @net_dev
  */
-#define hdd_vdev_op_start(net_dev) __hdd_vdev_op_start(net_dev, __func__)
+struct hdd_vdev_sync *hdd_vdev_sync_unregister(struct net_device *net_dev);
 
 /**
- * hdd_vdev_op_start_with_wdev() - attempt to start a vdev-level driver
- *	operation using a wireless_dev instance
- * @wdev: the wireless_dev to start the operation on
+ * hdd_vdev_sync_trans_start() - attempt to start a transition on @net_dev
+ * @net_dev: the net_device to transition
+ * @out_vdev_sync: out parameter for the synchronization context registered with
+ *	@net_dev, populated on success
  *
- * Return: an operation handle on success, NULL on failure
+ * Return: Errno
  */
-#define hdd_vdev_op_start_with_wdev(wdev) hdd_vdev_op_start((wdev)->netdev)
+#define hdd_vdev_sync_trans_start(net_dev, out_vdev_sync) \
+	__hdd_vdev_sync_trans_start(net_dev, out_vdev_sync, __func__)
 
-struct hdd_vdev_op *__hdd_vdev_op_start(struct net_device *net_dev,
-					const char *func);
+qdf_must_check int
+__hdd_vdev_sync_trans_start(struct net_device *net_dev,
+			    struct hdd_vdev_sync **out_vdev_sync,
+			    const char *desc);
 
 /**
- * hdd_vdev_op_stop() - stop a given vdev-level operation
- * @op: an operation handle identifying the operation to stop
+ * hdd_vdev_sync_trans_start_wait() - attempt to start a transition on @net_dev,
+ *	blocking if a conflicting transition is in flight
+ * @net_dev: the net_device to transition
+ * @out_vdev_sync: out parameter for the synchronization context registered with
+ *	@net_dev, populated on success
  *
- * Return: None
+ * Return: Errno
+ */
+#define hdd_vdev_sync_trans_start_wait(net_dev, out_vdev_sync) \
+	__hdd_vdev_sync_trans_start_wait(net_dev, out_vdev_sync, __func__)
+
+qdf_must_check int
+__hdd_vdev_sync_trans_start_wait(struct net_device *net_dev,
+				 struct hdd_vdev_sync **out_vdev_sync,
+				 const char *desc);
+
+/**
+ * hdd_vdev_sync_trans_stop() - stop a transition associated with @vdev_sync
+ * @vdev_sync: the synchonization context tracking the transition
+ *
+ * Return: none
  */
-#define hdd_vdev_op_stop(op) __hdd_vdev_op_stop(op, __func__)
+void hdd_vdev_sync_trans_stop(struct hdd_vdev_sync *vdev_sync);
 
-void __hdd_vdev_op_stop(struct hdd_vdev_op *op, const char *func);
+/**
+ * hdd_vdev_sync_assert_trans_protected() - assert that @net_dev is currently
+ *	protected by a transition
+ * @net_dev: the net_device to check
+ *
+ * Return: none
+ */
+void hdd_vdev_sync_assert_trans_protected(struct net_device *net_dev);
+
+/**
+ * hdd_vdev_sync_op_start() - attempt to start an operation on @net_dev
+ * @net_dev: the net_device to operate against
+ * @out_vdev_sync: out parameter for the synchronization context registered with
+ *	@net_dev, populated on success
+ *
+ * Return: Errno
+ */
+#define hdd_vdev_sync_op_start(net_dev, out_vdev_sync) \
+	__hdd_vdev_sync_op_start(net_dev, out_vdev_sync, __func__)
+
+qdf_must_check int
+__hdd_vdev_sync_op_start(struct net_device *net_dev,
+			 struct hdd_vdev_sync **out_vdev_sync,
+			 const char *func);
+
+/**
+ * hdd_vdev_sync_op_stop() - stop an operation associated with @vdev_sync
+ * @vdev_sync: the synchonization context tracking the operation
+ *
+ * Return: none
+ */
+#define hdd_vdev_sync_op_stop(net_dev) \
+	__hdd_vdev_sync_op_stop(net_dev, __func__)
+
+void __hdd_vdev_sync_op_stop(struct hdd_vdev_sync *vdev_sync,
+			     const char *func);
+
+/**
+ * hdd_vdev_sync_wait_for_ops() - wait until all @vdev_sync operations complete
+ * @vdev_sync: the synchonization context tracking the operations
+ *
+ * Return: None
+ */
+void hdd_vdev_sync_wait_for_ops(struct hdd_vdev_sync *vdev_sync);
 
 #endif

+ 221 - 68
core/hdd/src/wlan_hdd_dsc.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ * 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
@@ -37,130 +37,283 @@ struct dsc_psoc *hdd_dsc_psoc_from_wiphy(struct wiphy *wiphy)
 }
 
 /**
- * struct hdd_vdev_kvp - net_device/dsc_vdev key-value-pair for mapping a
- *	net_device to a dsc_vdev
- * @net_dev: the net_device key
- * @dsc_vdev: the dsc_vdev value
+ * struct hdd_vdev_sync - a vdev synchronization context
+ * @net_dev: the net_device used as a lookup key
+ * @dsc_vdev: the dsc_vdev used for synchronization
+ * @in_use: indicates if the context is being used
  */
-struct hdd_vdev_kvp {
+struct hdd_vdev_sync {
 	struct net_device *net_dev;
 	struct dsc_vdev *dsc_vdev;
+	bool in_use;
 };
 
-static struct hdd_vdev_kvp __hdd_vdev_map[CSR_ROAM_SESSION_MAX];
-static qdf_spinlock_t __hdd_vdev_lock;
+static struct hdd_vdev_sync __hdd_vdev_sync_arr[CSR_ROAM_SESSION_MAX];
+static qdf_spinlock_t __hdd_vdev_sync_lock;
 
-#define hdd_vdev_map_lock() qdf_spin_lock_bh(&__hdd_vdev_lock)
-#define hdd_vdev_map_unlock() qdf_spin_unlock_bh(&__hdd_vdev_lock)
-#define hdd_vdev_map_lock_assert() QDF_BUG(qdf_spin_is_locked(&__hdd_vdev_lock))
+#define hdd_vdev_sync_lock_create() qdf_spinlock_create(&__hdd_vdev_sync_lock)
+#define hdd_vdev_sync_lock_destroy() qdf_spinlock_destroy(&__hdd_vdev_sync_lock)
+#define hdd_vdev_sync_lock() qdf_spin_lock_bh(&__hdd_vdev_sync_lock)
+#define hdd_vdev_sync_unlock() qdf_spin_unlock_bh(&__hdd_vdev_sync_lock)
+#define hdd_vdev_sync_lock_assert() \
+	QDF_BUG(qdf_spin_is_locked(&__hdd_vdev_sync_lock))
 
-static struct hdd_vdev_kvp *hdd_vdev_kvp_get(struct net_device *net_dev)
+static struct hdd_vdev_sync *hdd_vdev_sync_lookup(struct net_device *net_dev)
 {
 	int i;
 
-	hdd_vdev_map_lock_assert();
+	hdd_vdev_sync_lock_assert();
 
-	for (i = 0; i < QDF_ARRAY_SIZE(__hdd_vdev_map); i++) {
-		struct hdd_vdev_kvp *kvp = __hdd_vdev_map + i;
+	for (i = 0; i < QDF_ARRAY_SIZE(__hdd_vdev_sync_arr); i++) {
+		struct hdd_vdev_sync *vdev_sync = __hdd_vdev_sync_arr + i;
 
-		if (kvp->net_dev == net_dev)
-			return kvp;
+		if (vdev_sync->net_dev == net_dev)
+			return vdev_sync;
 	}
 
 	return NULL;
 }
 
-void hdd_vdev_ops_register(struct net_device *net_dev,
-			   struct dsc_vdev *dsc_vdev)
+static struct hdd_vdev_sync *hdd_vdev_sync_get(void)
 {
-	struct hdd_vdev_kvp *kvp;
-
-	QDF_BUG(net_dev);
-	if (!net_dev)
-		return;
+	int i;
 
-	QDF_BUG(dsc_vdev);
-	if (!dsc_vdev)
-		return;
+	hdd_vdev_sync_lock_assert();
 
-	hdd_vdev_map_lock();
+	for (i = 0; i < QDF_ARRAY_SIZE(__hdd_vdev_sync_arr); i++) {
+		struct hdd_vdev_sync *vdev_sync = __hdd_vdev_sync_arr + i;
 
-	kvp = hdd_vdev_kvp_get(NULL);
-	QDF_BUG(kvp);
-	if (kvp) {
-		kvp->net_dev = net_dev;
-		kvp->dsc_vdev = dsc_vdev;
+		if (!vdev_sync->in_use) {
+			vdev_sync->in_use = true;
+			return vdev_sync;
+		}
 	}
 
-	hdd_vdev_map_unlock();
+	return NULL;
 }
 
-void hdd_vdev_ops_unregister(struct net_device *net_dev)
+static void hdd_vdev_sync_put(struct hdd_vdev_sync *vdev_sync)
 {
-	struct hdd_vdev_kvp *kvp;
+	hdd_vdev_sync_lock_assert();
 
-	QDF_BUG(net_dev);
-	if (!net_dev)
-		return;
+	qdf_mem_zero(vdev_sync, sizeof(*vdev_sync));
+}
 
-	hdd_vdev_map_lock();
+void hdd_dsc_init(void)
+{
+	hdd_vdev_sync_lock_create();
+}
 
-	kvp = hdd_vdev_kvp_get(net_dev);
-	QDF_BUG(kvp);
-	if (kvp) {
-		kvp->net_dev = NULL;
-		kvp->dsc_vdev = NULL;
-	}
+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)
+{
+	QDF_STATUS status;
+	struct dsc_psoc *dsc_psoc;
+	struct hdd_vdev_sync *vdev_sync;
+
+	QDF_BUG(wiphy);
+	if (!wiphy)
+		return -EINVAL;
+
+	QDF_BUG(out_vdev_sync);
+	if (!out_vdev_sync)
+		return -EINVAL;
+
+	dsc_psoc = hdd_dsc_psoc_from_wiphy(wiphy);
+	if (!dsc_psoc)
+		return -EINVAL;
+
+	hdd_vdev_sync_lock();
+	vdev_sync = hdd_vdev_sync_get();
+	hdd_vdev_sync_unlock();
+	if (!vdev_sync)
+		return -ENOMEM;
+
+	status = dsc_vdev_create(dsc_psoc, &vdev_sync->dsc_vdev);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto sync_put;
+
+	*out_vdev_sync = vdev_sync;
+
+	return 0;
+
+sync_put:
+	hdd_vdev_sync_lock();
+	hdd_vdev_sync_put(vdev_sync);
+	hdd_vdev_sync_unlock();
+
+	return qdf_status_to_os_return(status);
+}
+
+int __hdd_vdev_sync_create_with_trans(struct wiphy *wiphy,
+				      struct hdd_vdev_sync **out_vdev_sync,
+				      const char *desc)
+{
+	struct hdd_vdev_sync *vdev_sync;
+	QDF_STATUS status;
+	int errno;
+
+	errno = hdd_vdev_sync_create(wiphy, &vdev_sync);
+	if (errno)
+		return errno;
+
+	status = dsc_vdev_trans_start(vdev_sync->dsc_vdev, desc);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto sync_destroy;
+
+	*out_vdev_sync = vdev_sync;
+
+	return 0;
 
-	hdd_vdev_map_unlock();
+sync_destroy:
+	hdd_vdev_sync_destroy(vdev_sync);
+
+	return qdf_status_to_os_return(status);
 }
 
-static struct hdd_vdev_op *hdd_vdev_to_op(struct dsc_vdev *dsc_vdev)
+void hdd_vdev_sync_destroy(struct hdd_vdev_sync *vdev_sync)
 {
-	return (struct hdd_vdev_op *)dsc_vdev;
+	QDF_BUG(vdev_sync);
+	if (!vdev_sync)
+		return;
+
+	dsc_vdev_destroy(&vdev_sync->dsc_vdev);
+
+	hdd_vdev_sync_lock();
+	hdd_vdev_sync_put(vdev_sync);
+	hdd_vdev_sync_unlock();
 }
 
-static struct dsc_vdev *hdd_op_to_vdev(struct hdd_vdev_op *op)
+void hdd_vdev_sync_register(struct net_device *net_dev,
+			    struct hdd_vdev_sync *vdev_sync)
 {
-	return (struct dsc_vdev *)op;
+	QDF_BUG(net_dev);
+	QDF_BUG(vdev_sync);
+	if (!vdev_sync)
+		return;
+
+	hdd_vdev_sync_lock();
+	vdev_sync->net_dev = net_dev;
+	hdd_vdev_sync_unlock();
 }
 
-struct hdd_vdev_op *__hdd_vdev_op_start(struct net_device *net_dev,
-					const char *func)
+struct hdd_vdev_sync *hdd_vdev_sync_unregister(struct net_device *net_dev)
 {
-	QDF_STATUS status = QDF_STATUS_E_NULL_VALUE;
-	struct hdd_vdev_kvp *kvp;
+	struct hdd_vdev_sync *vdev_sync;
 
 	QDF_BUG(net_dev);
 	if (!net_dev)
 		return NULL;
 
-	hdd_vdev_map_lock();
+	hdd_vdev_sync_lock();
+	vdev_sync = hdd_vdev_sync_lookup(net_dev);
+	vdev_sync->net_dev = NULL;
+	hdd_vdev_sync_unlock();
 
-	kvp = hdd_vdev_kvp_get(net_dev);
-	if (kvp)
-		status = _dsc_vdev_op_start(kvp->dsc_vdev, func);
+	return vdev_sync;
+}
 
-	hdd_vdev_map_unlock();
+typedef QDF_STATUS (*vdev_start_func)(struct dsc_vdev *, const char *);
+
+static int __hdd_vdev_sync_start_callback(struct net_device *net_dev,
+					  struct hdd_vdev_sync **out_vdev_sync,
+					  const char *desc,
+					  vdev_start_func vdev_start_cb)
+{
+	QDF_STATUS status;
+	struct hdd_vdev_sync *vdev_sync;
 
+	hdd_vdev_sync_lock_assert();
+
+	*out_vdev_sync = NULL;
+
+	vdev_sync = hdd_vdev_sync_lookup(net_dev);
+	if (!vdev_sync)
+		return -EAGAIN;
+
+	status = vdev_start_cb(vdev_sync->dsc_vdev, desc);
 	if (QDF_IS_STATUS_ERROR(status))
-		return NULL;
+		return qdf_status_to_os_return(status);
+
+	*out_vdev_sync = vdev_sync;
 
-	return hdd_vdev_to_op(kvp->dsc_vdev);
+	return 0;
 }
 
-void __hdd_vdev_op_stop(struct hdd_vdev_op *op, const char *func)
+int __hdd_vdev_sync_trans_start(struct net_device *net_dev,
+				struct hdd_vdev_sync **out_vdev_sync,
+				const char *desc)
 {
-	_dsc_vdev_op_stop(hdd_op_to_vdev(op), func);
+	int errno;
+
+	hdd_vdev_sync_lock();
+	errno = __hdd_vdev_sync_start_callback(net_dev, out_vdev_sync, desc,
+					       dsc_vdev_trans_start);
+	hdd_vdev_sync_unlock();
+
+	return errno;
 }
 
-void hdd_dsc_init(void)
+int __hdd_vdev_sync_trans_start_wait(struct net_device *net_dev,
+				     struct hdd_vdev_sync **out_vdev_sync,
+				     const char *desc)
+{
+	int errno;
+
+	hdd_vdev_sync_lock();
+	errno = __hdd_vdev_sync_start_callback(net_dev, out_vdev_sync, desc,
+					       dsc_vdev_trans_start_wait);
+	hdd_vdev_sync_unlock();
+
+	return errno;
+}
+
+void hdd_vdev_sync_trans_stop(struct hdd_vdev_sync *vdev_sync)
 {
-	qdf_spinlock_create(&__hdd_vdev_lock);
+	dsc_vdev_trans_stop(vdev_sync->dsc_vdev);
 }
 
-void hdd_dsc_deinit(void)
+void hdd_vdev_sync_assert_trans_protected(struct net_device *net_dev)
+{
+	struct hdd_vdev_sync *vdev_sync;
+
+	hdd_vdev_sync_lock();
+
+	vdev_sync = hdd_vdev_sync_lookup(net_dev);
+	QDF_BUG(vdev_sync);
+	if (vdev_sync)
+		dsc_vdev_assert_trans_protected(vdev_sync->dsc_vdev);
+
+	hdd_vdev_sync_unlock();
+}
+
+int __hdd_vdev_sync_op_start(struct net_device *net_dev,
+			     struct hdd_vdev_sync **out_vdev_sync,
+			     const char *func)
+{
+	int errno;
+
+	hdd_vdev_sync_lock();
+	errno = __hdd_vdev_sync_start_callback(net_dev, out_vdev_sync, func,
+					       _dsc_vdev_op_start);
+	hdd_vdev_sync_unlock();
+
+	return errno;
+}
+
+void __hdd_vdev_sync_op_stop(struct hdd_vdev_sync *vdev_sync,
+			     const char *func)
+{
+	_dsc_vdev_op_stop(vdev_sync->dsc_vdev, func);
+}
+
+void hdd_vdev_sync_wait_for_ops(struct hdd_vdev_sync *vdev_sync)
 {
-	qdf_spinlock_destroy(&__hdd_vdev_lock);
+	dsc_vdev_wait_for_ops(vdev_sync->dsc_vdev);
 }