From 6661df31047b9fdc5ccd30bf82e1675321be88cc Mon Sep 17 00:00:00 2001 From: Dustin Brown Date: Fri, 6 Jul 2018 14:31:12 -0700 Subject: [PATCH] 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 --- components/dsc/test/wlan_dsc_test.c | 552 ++++++++++++++++++++++++++++ 1 file changed, 552 insertions(+) diff --git a/components/dsc/test/wlan_dsc_test.c b/components/dsc/test/wlan_dsc_test.c index 7bbea0cc1197..f7d828adb44a 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; +} +