qcacld-3.0: Add Driver Synchronization Core (unit test)

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 5, add the unit test implementations.

Change-Id: Ia68d6df18894b254c1f5fe3855d896e96be38a90
CRs-Fixed: 2290260
diff --git a/components/dsc/test/wlan_dsc_test.c b/components/dsc/test/wlan_dsc_test.c
index 7bbea0c..f7d828a 100644
--- a/components/dsc/test/wlan_dsc_test.c
+++ b/components/dsc/test/wlan_dsc_test.c
@@ -16,3 +16,555 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "__wlan_dsc.h"
+#include "qdf_event.h"
+#include "qdf_threads.h"
+#include "qdf_trace.h"
+#include "qdf_types.h"
+#include "wlan_dsc.h"
+#include "wlan_dsc_test.h"
+
+#define dsc_driver_trans_start(driver) dsc_driver_trans_start(driver, "")
+#define dsc_psoc_trans_start(psoc) dsc_psoc_trans_start(psoc, "")
+#define dsc_vdev_trans_start(vdev) dsc_vdev_trans_start(vdev, "")
+
+#define dsc_driver_trans_start_wait(driver) \
+	dsc_driver_trans_start_wait(driver, "")
+#define dsc_psoc_trans_start_wait(psoc) dsc_psoc_trans_start_wait(psoc, "")
+#define dsc_vdev_trans_start_wait(vdev) dsc_vdev_trans_start_wait(vdev, "")
+
+static struct dsc_psoc *nth_psoc(struct dsc_driver *driver, int n)
+{
+	struct dsc_psoc *psoc;
+
+	QDF_BUG(n > 0);
+	if (n <= 0)
+		return NULL;
+
+	dsc_for_each_driver_psoc(driver, psoc) {
+		n--;
+		if (n)
+			continue;
+
+		return psoc;
+	}
+
+	QDF_DEBUG_PANIC();
+
+	return NULL;
+}
+
+static struct dsc_vdev *nth_vdev(struct dsc_psoc *psoc, int n)
+{
+	struct dsc_vdev *vdev;
+
+	QDF_BUG(n > 0);
+	if (n <= 0)
+		return NULL;
+
+	dsc_for_each_psoc_vdev(psoc, vdev) {
+		n--;
+		if (n)
+			continue;
+
+		return vdev;
+	}
+
+	QDF_DEBUG_PANIC();
+
+	return NULL;
+}
+
+static void __dsc_tree_destroy(struct dsc_driver *driver)
+{
+	struct dsc_psoc *psoc;
+	struct dsc_psoc *next_psoc;
+
+	QDF_BUG(driver);
+
+	qdf_list_for_each_del(&driver->psocs, psoc, next_psoc, node) {
+		struct dsc_vdev *vdev;
+		struct dsc_vdev *next_vdev;
+
+		qdf_list_for_each_del(&psoc->vdevs, vdev, next_vdev, node)
+			dsc_vdev_destroy(&vdev);
+
+		dsc_psoc_destroy(&psoc);
+	}
+
+	dsc_driver_destroy(&driver);
+}
+
+static QDF_STATUS __dsc_tree_create(struct dsc_driver **out_driver,
+				    uint8_t psocs_per_driver,
+				    uint8_t vdevs_per_psoc)
+{
+	QDF_STATUS status;
+	struct dsc_driver *driver;
+	int i, j;
+
+	status = dsc_driver_create(&driver);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		dsc_err("Failed to create driver; status:%u", status);
+		return status;
+	}
+
+	for (i = 0; i < psocs_per_driver; i++) {
+		struct dsc_psoc *psoc;
+
+		status = dsc_psoc_create(driver, &psoc);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			dsc_err("Failed to create psoc; status:%u", status);
+			goto free_tree;
+		}
+
+		for (j = 0; j < vdevs_per_psoc; j++) {
+			struct dsc_vdev *vdev;
+
+			status = dsc_vdev_create(psoc, &vdev);
+			if (QDF_IS_STATUS_ERROR(status)) {
+				dsc_err("Failed to create vdev; status:%u",
+					status);
+				goto free_tree;
+			}
+		}
+	}
+
+	*out_driver = driver;
+
+	return QDF_STATUS_SUCCESS;
+
+free_tree:
+	__dsc_tree_destroy(driver);
+
+	return status;
+}
+
+static uint32_t dsc_test_create_destroy(void)
+{
+	uint32_t errors = 0;
+	QDF_STATUS status;
+	struct dsc_driver *driver;
+
+	dsc_enter();
+
+	status = __dsc_tree_create(&driver, 2, 2);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		errors++;
+		goto exit;
+	}
+
+	__dsc_tree_destroy(driver);
+
+exit:
+	dsc_exit();
+
+	return errors;
+}
+
+#define action_expect(obj, action, status, errors) \
+do { \
+	void *__obj = obj; \
+	QDF_STATUS __expected = status; \
+	QDF_STATUS __result; \
+\
+	__result = dsc_##obj##_##action##_start(__obj); \
+	if (__result != __expected) { \
+		dsc_err("FAIL: " #obj " " #action \
+			"; expected " #status " (%u), found %u", \
+			__expected, __result); \
+		(errors)++; \
+	} \
+	if (QDF_IS_STATUS_SUCCESS(__result) && QDF_IS_STATUS_ERROR(__expected))\
+		dsc_##obj##_##action##_stop(__obj); \
+} while (false)
+
+static uint32_t dsc_test_driver_trans_blocks(void)
+{
+	uint32_t errors = 0;
+	QDF_STATUS status;
+	struct dsc_driver *driver;
+	struct dsc_psoc *psoc;
+	struct dsc_vdev *vdev;
+
+	dsc_enter();
+
+	/* setup */
+
+	status = __dsc_tree_create(&driver, 2, 2);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		errors++;
+		goto exit;
+	}
+
+	action_expect(driver, trans, QDF_STATUS_SUCCESS, errors);
+
+	/* test */
+
+	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
+	action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
+
+	dsc_for_each_driver_psoc(driver, psoc) {
+		action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
+		action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
+
+		dsc_for_each_psoc_vdev(psoc, vdev) {
+			action_expect(vdev, trans, QDF_STATUS_E_AGAIN, errors);
+			action_expect(vdev, op, QDF_STATUS_E_AGAIN, errors);
+		}
+	}
+
+	/* teardown */
+
+	dsc_driver_trans_stop(driver);
+
+	__dsc_tree_destroy(driver);
+
+exit:
+	dsc_exit();
+
+	return errors;
+}
+
+static uint32_t dsc_test_psoc_trans_blocks(void)
+{
+	uint32_t errors = 0;
+	QDF_STATUS status;
+	struct dsc_driver *driver;
+	struct dsc_psoc *psoc;
+	struct dsc_vdev *vdev;
+
+	dsc_enter();
+
+	/* setup */
+
+	status = __dsc_tree_create(&driver, 2, 2);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		errors++;
+		goto exit;
+	}
+
+	/* test */
+
+	psoc = nth_psoc(driver, 1);
+	action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors);
+
+	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
+	action_expect(driver, op, QDF_STATUS_SUCCESS, errors);
+	dsc_driver_op_stop(driver);
+
+	action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
+	action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
+
+	dsc_for_each_psoc_vdev(psoc, vdev) {
+		action_expect(vdev, trans, QDF_STATUS_E_AGAIN, errors);
+		action_expect(vdev, op, QDF_STATUS_E_AGAIN, errors);
+	}
+
+	psoc = nth_psoc(driver, 2);
+	action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors);
+
+	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
+	action_expect(driver, op, QDF_STATUS_SUCCESS, errors);
+	dsc_driver_op_stop(driver);
+
+	action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
+	action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
+
+	dsc_for_each_psoc_vdev(psoc, vdev) {
+		action_expect(vdev, trans, QDF_STATUS_E_AGAIN, errors);
+		action_expect(vdev, op, QDF_STATUS_E_AGAIN, errors);
+	}
+
+	/* teardown */
+
+	dsc_for_each_driver_psoc(driver, psoc)
+		dsc_psoc_trans_stop(psoc);
+
+	__dsc_tree_destroy(driver);
+
+exit:
+	dsc_exit();
+
+	return errors;
+}
+
+static uint32_t dsc_test_vdev_trans_blocks(void)
+{
+	uint32_t errors = 0;
+	QDF_STATUS status;
+	struct dsc_driver *driver;
+	struct dsc_psoc *psoc;
+	struct dsc_vdev *vdev;
+
+	dsc_enter();
+
+	/* setup */
+
+	status = __dsc_tree_create(&driver, 2, 2);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		errors++;
+		goto exit;
+	}
+
+	dsc_for_each_driver_psoc(driver, psoc) {
+		dsc_for_each_psoc_vdev(psoc, vdev)
+			action_expect(vdev, trans, QDF_STATUS_SUCCESS, errors);
+	}
+
+	/* test */
+
+	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
+	action_expect(driver, op, QDF_STATUS_SUCCESS, errors);
+	dsc_driver_op_stop(driver);
+
+	dsc_for_each_driver_psoc(driver, psoc) {
+		action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
+		action_expect(psoc, op, QDF_STATUS_SUCCESS, errors);
+		dsc_psoc_op_stop(psoc);
+
+		dsc_for_each_psoc_vdev(psoc, vdev) {
+			action_expect(vdev, trans, QDF_STATUS_E_AGAIN, errors);
+			action_expect(vdev, op, QDF_STATUS_E_AGAIN, errors);
+		}
+	}
+
+	/* teardown */
+
+	dsc_for_each_driver_psoc(driver, psoc) {
+		dsc_for_each_psoc_vdev(psoc, vdev)
+			dsc_vdev_trans_stop(vdev);
+	}
+
+	__dsc_tree_destroy(driver);
+
+exit:
+	dsc_exit();
+
+	return errors;
+}
+
+#define THREAD_TIMEOUT 1000 /* ms */
+#define dsc_event_wait(event) qdf_wait_single_event(event, THREAD_TIMEOUT)
+
+struct thread_ctx {
+	struct dsc_driver *driver;
+	qdf_event_t start_vdev_trans;
+	qdf_event_t start_vdev_wait;
+	qdf_event_t start_psoc_wait;
+	qdf_event_t start_driver_wait;
+	qdf_event_t stop_ops;
+	uint32_t step;
+};
+
+static QDF_STATUS dsc_thread_ops(void *context)
+{
+	struct thread_ctx *ctx = context;
+	struct dsc_driver *driver = ctx->driver;
+	struct dsc_psoc *psoc = nth_psoc(driver, 1);
+	struct dsc_vdev *vdev = nth_vdev(psoc, 1);
+
+	dsc_enter();
+
+	dsc_assert_success(dsc_driver_op_start(driver));
+	dsc_assert_success(dsc_psoc_op_start(psoc));
+	dsc_assert_success(dsc_vdev_op_start(vdev));
+
+	dsc_assert(++ctx->step == 1);
+	qdf_event_set(&ctx->start_vdev_trans);
+
+	dsc_assert_success(dsc_event_wait(&ctx->stop_ops));
+	dsc_assert(++ctx->step == 7);
+
+	dsc_driver_op_stop(driver);
+	schedule();
+	dsc_psoc_op_stop(psoc);
+	schedule();
+	dsc_vdev_op_stop(vdev);
+
+	dsc_exit();
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS dsc_thread_vdev_trans(void *context)
+{
+	struct thread_ctx *ctx = context;
+	struct dsc_driver *driver = ctx->driver;
+	struct dsc_psoc *psoc = nth_psoc(driver, 1);
+	struct dsc_vdev *vdev;
+
+	dsc_enter();
+
+	dsc_assert_success(dsc_event_wait(&ctx->start_vdev_trans));
+	dsc_assert(++ctx->step == 2);
+
+	dsc_for_each_psoc_vdev(psoc, vdev)
+		dsc_assert_success(dsc_vdev_trans_start(vdev));
+
+	dsc_assert(++ctx->step == 3);
+	qdf_event_set(&ctx->start_vdev_wait);
+
+	dsc_vdev_wait_for_ops(nth_vdev(psoc, 1));
+
+	dsc_assert(++ctx->step == 8);
+	dsc_vdev_trans_stop(nth_vdev(psoc, 1));
+	schedule();
+	dsc_assert(++ctx->step == 9);
+
+	dsc_vdev_trans_stop(nth_vdev(psoc, 2));
+
+	dsc_exit();
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS dsc_thread_vdev_wait(void *context)
+{
+	struct thread_ctx *ctx = context;
+	struct dsc_vdev *vdev = nth_vdev(nth_psoc(ctx->driver, 1), 1);
+
+	dsc_enter();
+
+	dsc_assert_success(dsc_event_wait(&ctx->start_vdev_wait));
+	dsc_assert(++ctx->step == 4);
+
+	qdf_event_set(&ctx->start_psoc_wait);
+	dsc_assert_success(dsc_vdev_trans_start_wait(vdev));
+	dsc_assert(++ctx->step == 14);
+
+	schedule();
+
+	dsc_assert(++ctx->step == 15);
+	dsc_vdev_trans_stop(vdev);
+
+	dsc_exit();
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS dsc_thread_psoc_wait(void *context)
+{
+	struct thread_ctx *ctx = context;
+	struct dsc_psoc *psoc = nth_psoc(ctx->driver, 1);
+
+	dsc_enter();
+
+	dsc_assert_success(dsc_event_wait(&ctx->start_psoc_wait));
+	dsc_assert(++ctx->step == 5);
+
+	qdf_event_set(&ctx->start_driver_wait);
+	dsc_assert_success(dsc_psoc_trans_start_wait(psoc));
+	dsc_assert(++ctx->step == 12);
+
+	schedule();
+
+	dsc_assert(++ctx->step == 13);
+	dsc_psoc_trans_stop(psoc);
+
+	dsc_exit();
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS dsc_thread_driver_wait(void *context)
+{
+	struct thread_ctx *ctx = context;
+	struct dsc_driver *driver = ctx->driver;
+
+	dsc_enter();
+
+	dsc_assert_success(dsc_event_wait(&ctx->start_driver_wait));
+	dsc_assert(++ctx->step == 6);
+
+	qdf_event_set(&ctx->stop_ops);
+	dsc_assert_success(dsc_driver_trans_start_wait(driver));
+	dsc_assert(++ctx->step == 10);
+
+	schedule();
+
+	dsc_assert(++ctx->step == 11);
+	dsc_driver_trans_stop(driver);
+
+	dsc_exit();
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static uint32_t dsc_test_trans_wait(void)
+{
+	uint32_t errors = 0;
+	QDF_STATUS status;
+	qdf_thread_t *ops_thread;
+	qdf_thread_t *vdev_trans_thread;
+	qdf_thread_t *vdev_wait_thread;
+	qdf_thread_t *psoc_wait_thread;
+	qdf_thread_t *driver_wait_thread;
+	struct thread_ctx ctx = { 0 };
+
+	dsc_enter();
+
+	status = __dsc_tree_create(&ctx.driver, 1, 2);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		errors++;
+		goto exit;
+	}
+
+	dsc_assert_success(qdf_event_create(&ctx.start_vdev_trans));
+	dsc_assert_success(qdf_event_create(&ctx.start_vdev_wait));
+	dsc_assert_success(qdf_event_create(&ctx.start_psoc_wait));
+	dsc_assert_success(qdf_event_create(&ctx.start_driver_wait));
+	dsc_assert_success(qdf_event_create(&ctx.stop_ops));
+
+	dsc_debug("starting threads");
+
+	ops_thread = qdf_thread_run(dsc_thread_ops, &ctx);
+	vdev_trans_thread = qdf_thread_run(dsc_thread_vdev_trans, &ctx);
+	vdev_wait_thread = qdf_thread_run(dsc_thread_vdev_wait, &ctx);
+	psoc_wait_thread = qdf_thread_run(dsc_thread_psoc_wait, &ctx);
+	driver_wait_thread = qdf_thread_run(dsc_thread_driver_wait, &ctx);
+
+	qdf_thread_join(ops_thread);
+	qdf_thread_join(vdev_trans_thread);
+	qdf_thread_join(vdev_wait_thread);
+	qdf_thread_join(psoc_wait_thread);
+	qdf_thread_join(driver_wait_thread);
+
+	dsc_debug("threads joined");
+
+	qdf_event_destroy(&ctx.stop_ops);
+	qdf_event_destroy(&ctx.start_driver_wait);
+	qdf_event_destroy(&ctx.start_psoc_wait);
+	qdf_event_destroy(&ctx.start_vdev_wait);
+	qdf_event_destroy(&ctx.start_vdev_trans);
+
+	__dsc_tree_destroy(ctx.driver);
+
+exit:
+	dsc_exit();
+
+	return errors;
+}
+
+uint32_t dsc_unit_test(void)
+{
+	uint32_t errors = 0;
+
+	dsc_debug("Starting dsc component tests");
+
+	errors += dsc_test_create_destroy();
+	errors += dsc_test_driver_trans_blocks();
+	errors += dsc_test_psoc_trans_blocks();
+	errors += dsc_test_vdev_trans_blocks();
+	errors += dsc_test_trans_wait();
+
+	if (errors) {
+		dsc_err("FAIL: %u dsc component tests failed!", errors);
+		return errors;
+	}
+
+	dsc_info("PASS: dsc component tests passed successfully");
+
+	return 0;
+}
+