On Thu, Sep 28, 2023 at 9:36 AM Mattias Rönnblom <mattias.ronnb...@ericsson.com> wrote: > > Add unit tests for the dispatcher.
Missing your SoB. Same comment than patch 1 about indent. > > -- > PATCH v5: > o Update test suite to use pointer and not integer id when calling > dispatcher functions. > > PATCH v3: > o Adapt the test suite to dispatcher API name changes. > > PATCH v2: > o Test finalize callback functionality. > o Test handler and finalizer count upper limits. > o Add statistics reset test. > o Make sure dispatcher supply the proper event dev id and port id back > to the application. > > PATCH: > o Extend test to cover often-used handler optimization feature. > > RFC v4: > o Adapt to non-const events in process function prototype. > > Signed-off-by: Mattias Rönnblom <mattias.ronnb...@ericsson.com> > --- > MAINTAINERS | 1 + > app/test/meson.build | 1 + > app/test/test_dispatcher.c | 1046 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 1048 insertions(+) > create mode 100644 app/test/test_dispatcher.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 6704cd5b2c..43890cad0e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1729,6 +1729,7 @@ F: lib/node/ > Dispatcher - EXPERIMENTAL > M: Mattias Rönnblom <mattias.ronnb...@ericsson.com> > F: lib/dispatcher/ > +F: app/test/test_dispatcher.c > > Test Applications > ----------------- > diff --git a/app/test/meson.build b/app/test/meson.build > index 05bae9216d..3303c73817 100644 > --- a/app/test/meson.build > +++ b/app/test/meson.build > @@ -55,6 +55,7 @@ source_file_deps = { > 'test_cycles.c': [], > 'test_debug.c': [], > 'test_devargs.c': ['kvargs'], > + 'test_dispatcher.c': ['dispatcher'], > 'test_distributor.c': ['distributor'], > 'test_distributor_perf.c': ['distributor'], > 'test_dmadev.c': ['dmadev', 'bus_vdev'], > diff --git a/app/test/test_dispatcher.c b/app/test/test_dispatcher.c > new file mode 100644 > index 0000000000..2bce65fdd9 > --- /dev/null > +++ b/app/test/test_dispatcher.c > @@ -0,0 +1,1046 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2023 Ericsson AB > + */ > + > +#include "test.h" test.h is a "local" include and should go after "standard" includes block and "DPDK" includes block. > + > +#include <stdatomic.h> Can you use the new wrappers for atomics? rte_stdatomic.h > + > +#include <rte_bus_vdev.h> > +#include <rte_dispatcher.h> > +#include <rte_eventdev.h> > +#include <rte_random.h> > +#include <rte_service.h> > + > +#define NUM_WORKERS 3 > + > +#define NUM_PORTS (NUM_WORKERS + 1) > +#define WORKER_PORT_ID(worker_idx) (worker_idx) > +#define DRIVER_PORT_ID (NUM_PORTS - 1) > + > +#define NUM_SERVICE_CORES NUM_WORKERS > + > +/* Eventdev */ > +#define NUM_QUEUES 8 > +#define LAST_QUEUE_ID (NUM_QUEUES - 1) > +#define MAX_EVENTS 4096 > +#define NEW_EVENT_THRESHOLD (MAX_EVENTS / 2) > +#define DEQUEUE_BURST_SIZE 32 > +#define ENQUEUE_BURST_SIZE 32 > + > +#define NUM_EVENTS 10000000 > +#define NUM_FLOWS 16 > + > +#define DSW_VDEV "event_dsw0" > + > +struct app_queue { > + uint8_t queue_id; > + uint64_t sn[NUM_FLOWS]; > + int dispatcher_reg_id; > +}; > + > +struct cb_count { > + uint8_t expected_event_dev_id; > + uint8_t expected_event_port_id[RTE_MAX_LCORE]; > + atomic_int count; > +}; > + > +struct test_app { > + uint8_t event_dev_id; > + struct rte_dispatcher *dispatcher; > + uint32_t dispatcher_service_id; > + > + unsigned int service_lcores[NUM_SERVICE_CORES]; > + > + int never_match_reg_id; > + uint64_t never_match_count; > + struct cb_count never_process_count; > + > + struct app_queue queues[NUM_QUEUES]; > + > + int finalize_reg_id; > + struct cb_count finalize_count; > + > + bool running; > + > + atomic_int completed_events; > + atomic_int errors; > +}; > + > +#define RETURN_ON_ERROR(rc) \ > + do { \ > + if (rc != TEST_SUCCESS) \ > + return rc; \ > + } while (0) TEST_ASSERT? This gives context about which part of a test failed. > + > +static struct test_app * > +test_app_create(void) > +{ > + int i; > + struct test_app *app; > + > + app = calloc(1, sizeof(struct test_app)); > + > + if (app == NULL) > + return NULL; > + > + for (i = 0; i < NUM_QUEUES; i++) > + app->queues[i].queue_id = i; > + > + return app; > +} > + > +static void > +test_app_free(struct test_app *app) > +{ > + free(app); > +} > + > +static int > +test_app_create_vdev(struct test_app *app) > +{ > + int rc; > + > + rc = rte_vdev_init(DSW_VDEV, NULL); > + if (rc < 0) > + return TEST_SKIPPED; > + > + rc = rte_event_dev_get_dev_id(DSW_VDEV); > + > + app->event_dev_id = (uint8_t)rc; > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_destroy_vdev(struct test_app *app) > +{ > + int rc; > + > + rc = rte_event_dev_close(app->event_dev_id); > + TEST_ASSERT_SUCCESS(rc, "Error while closing event device"); > + > + rc = rte_vdev_uninit(DSW_VDEV); > + TEST_ASSERT_SUCCESS(rc, "Error while uninitializing virtual device"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_setup_event_dev(struct test_app *app) > +{ > + int rc; > + int i; > + > + rc = test_app_create_vdev(app); > + if (rc < 0) > + return rc; > + > + struct rte_event_dev_config config = { > + .nb_event_queues = NUM_QUEUES, > + .nb_event_ports = NUM_PORTS, > + .nb_events_limit = MAX_EVENTS, > + .nb_event_queue_flows = 64, > + .nb_event_port_dequeue_depth = DEQUEUE_BURST_SIZE, > + .nb_event_port_enqueue_depth = ENQUEUE_BURST_SIZE > + }; > + > + rc = rte_event_dev_configure(app->event_dev_id, &config); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to configure event device"); > + > + struct rte_event_queue_conf queue_config = { > + .priority = RTE_EVENT_DEV_PRIORITY_NORMAL, > + .schedule_type = RTE_SCHED_TYPE_ATOMIC, > + .nb_atomic_flows = 64 > + }; > + > + for (i = 0; i < NUM_QUEUES; i++) { > + uint8_t queue_id = i; > + > + rc = rte_event_queue_setup(app->event_dev_id, queue_id, > + &queue_config); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to setup queue %d", queue_id); > + } > + > + struct rte_event_port_conf port_config = { > + .new_event_threshold = NEW_EVENT_THRESHOLD, > + .dequeue_depth = DEQUEUE_BURST_SIZE, > + .enqueue_depth = ENQUEUE_BURST_SIZE > + }; > + > + for (i = 0; i < NUM_PORTS; i++) { > + uint8_t event_port_id = i; > + > + rc = rte_event_port_setup(app->event_dev_id, event_port_id, > + &port_config); > + TEST_ASSERT_SUCCESS(rc, "Failed to create event port %d", > + event_port_id); > + > + if (event_port_id == DRIVER_PORT_ID) > + continue; > + > + rc = rte_event_port_link(app->event_dev_id, event_port_id, > + NULL, NULL, 0); > + > + TEST_ASSERT_EQUAL(rc, NUM_QUEUES, "Failed to link port %d", > + event_port_id); > + } > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_teardown_event_dev(struct test_app *app) > +{ > + return test_app_destroy_vdev(app); > +} > + > +static int > +test_app_start_event_dev(struct test_app *app) > +{ > + int rc; > + > + rc = rte_event_dev_start(app->event_dev_id); > + TEST_ASSERT_SUCCESS(rc, "Unable to start event device"); > + > + return TEST_SUCCESS; > +} > + > +static void > +test_app_stop_event_dev(struct test_app *app) > +{ > + rte_event_dev_stop(app->event_dev_id); > +} > + > +static int > +test_app_create_dispatcher(struct test_app *app) > +{ > + int rc; > + > + app->dispatcher = rte_dispatcher_create(app->event_dev_id); > + > + TEST_ASSERT(app->dispatcher != NULL, "Unable to create event " > + "dispatcher"); > + > + app->dispatcher_service_id = > + rte_dispatcher_service_id_get(app->dispatcher); > + > + rc = rte_service_set_stats_enable(app->dispatcher_service_id, 1); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to enable event dispatcher service " > + "stats"); > + > + rc = rte_service_runstate_set(app->dispatcher_service_id, 1); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to set dispatcher service runstate"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_free_dispatcher(struct test_app *app) > +{ > + int rc; > + > + rc = rte_service_runstate_set(app->dispatcher_service_id, 0); > + TEST_ASSERT_SUCCESS(rc, "Error disabling dispatcher service"); > + > + rc = rte_dispatcher_free(app->dispatcher); > + TEST_ASSERT_SUCCESS(rc, "Error freeing dispatcher"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_bind_ports(struct test_app *app) > +{ > + int i; > + > + app->never_process_count.expected_event_dev_id = > + app->event_dev_id; > + app->finalize_count.expected_event_dev_id = > + app->event_dev_id; > + > + for (i = 0; i < NUM_WORKERS; i++) { > + unsigned int lcore_id = app->service_lcores[i]; > + uint8_t port_id = WORKER_PORT_ID(i); > + > + int rc = rte_dispatcher_bind_port_to_lcore( > + app->dispatcher, port_id, DEQUEUE_BURST_SIZE, 0, > + lcore_id > + ); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to bind event device port %d " > + "to lcore %d", port_id, lcore_id); > + > + app->never_process_count.expected_event_port_id[lcore_id] = > + port_id; > + app->finalize_count.expected_event_port_id[lcore_id] = > port_id; > + } > + > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_unbind_ports(struct test_app *app) > +{ > + int i; > + > + for (i = 0; i < NUM_WORKERS; i++) { > + unsigned int lcore_id = app->service_lcores[i]; > + > + int rc = rte_dispatcher_unbind_port_from_lcore( > + app->dispatcher, > + WORKER_PORT_ID(i), > + lcore_id > + ); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to unbind event device port > %d " > + "from lcore %d", WORKER_PORT_ID(i), > + lcore_id); > + } > + > + return TEST_SUCCESS; > +} > + > +static bool > +match_queue(const struct rte_event *event, void *cb_data) > +{ > + uintptr_t queue_id = (uintptr_t)cb_data; > + > + return event->queue_id == queue_id; > +} > + > +static int > +test_app_get_worker_index(struct test_app *app, unsigned int lcore_id) > +{ > + int i; > + > + for (i = 0; i < NUM_SERVICE_CORES; i++) > + if (app->service_lcores[i] == lcore_id) > + return i; This construct is hard to read and prone to error if the code is updated later. for () { if () return i; } > + > + return -1; > +} > + > +static int > +test_app_get_worker_port(struct test_app *app, unsigned int lcore_id) > +{ > + int worker; > + > + worker = test_app_get_worker_index(app, lcore_id); > + > + if (worker < 0) > + return -1; > + > + return WORKER_PORT_ID(worker); > +} > + > +static void > +test_app_queue_note_error(struct test_app *app) > +{ > + atomic_fetch_add_explicit(&app->errors, 1, memory_order_relaxed); > +} > + > +static void > +test_app_process_queue(uint8_t p_event_dev_id, uint8_t p_event_port_id, > + struct rte_event *in_events, uint16_t num, > + void *cb_data) > +{ > + struct app_queue *app_queue = cb_data; > + struct test_app *app = container_of(app_queue, struct test_app, > + queues[app_queue->queue_id]); > + unsigned int lcore_id = rte_lcore_id(); > + bool intermediate_queue = app_queue->queue_id != LAST_QUEUE_ID; > + int event_port_id; > + uint16_t i; > + struct rte_event out_events[num]; > + > + event_port_id = test_app_get_worker_port(app, lcore_id); > + > + if (event_port_id < 0 || p_event_dev_id != app->event_dev_id || > + p_event_port_id != event_port_id) { > + test_app_queue_note_error(app); > + return; > + } > + > + for (i = 0; i < num; i++) { > + const struct rte_event *in_event = &in_events[i]; > + struct rte_event *out_event = &out_events[i]; > + uint64_t sn = in_event->u64; > + uint64_t expected_sn; > + > + if (in_event->queue_id != app_queue->queue_id) { > + test_app_queue_note_error(app); > + return; > + } > + > + expected_sn = app_queue->sn[in_event->flow_id]++; > + > + if (expected_sn != sn) { > + test_app_queue_note_error(app); > + return; > + } > + > + if (intermediate_queue) > + *out_event = (struct rte_event) { > + .queue_id = in_event->queue_id + 1, > + .flow_id = in_event->flow_id, > + .sched_type = RTE_SCHED_TYPE_ATOMIC, > + .op = RTE_EVENT_OP_FORWARD, > + .priority = RTE_EVENT_DEV_PRIORITY_NORMAL, > + .u64 = sn > + }; > + } > + > + if (intermediate_queue) { > + uint16_t n = 0; > + > + do { > + n += rte_event_enqueue_forward_burst(p_event_dev_id, > + p_event_port_id, > + out_events + n, > + num - n); > + } while (n != num); > + } else > + atomic_fetch_add_explicit(&app->completed_events, num, > + memory_order_relaxed); > +} > + > +static bool > +never_match(const struct rte_event *event __rte_unused, void *cb_data) > +{ > + uint64_t *count = cb_data; > + > + (*count)++; > + > + return false; > +} > + > +static void > +test_app_never_process(uint8_t event_dev_id, > + uint8_t event_port_id, > + struct rte_event *in_events __rte_unused, > + uint16_t num, void *cb_data) > +{ > + struct cb_count *count = cb_data; > + unsigned int lcore_id = rte_lcore_id(); > + > + if (event_dev_id == count->expected_event_dev_id && > + event_port_id == count->expected_event_port_id[lcore_id]) > + atomic_fetch_add_explicit(&count->count, num, > + memory_order_relaxed); > +} > + > +static void > +finalize(uint8_t event_dev_id, uint8_t event_port_id, void *cb_data) > +{ > + struct cb_count *count = cb_data; > + unsigned int lcore_id = rte_lcore_id(); > + > + if (event_dev_id == count->expected_event_dev_id && > + event_port_id == count->expected_event_port_id[lcore_id]) > + atomic_fetch_add_explicit(&count->count, 1, > + memory_order_relaxed); > +} > + > +static int > +test_app_register_callbacks(struct test_app *app) > +{ > + int i; > + > + app->never_match_reg_id = > + rte_dispatcher_register(app->dispatcher, never_match, > + &app->never_match_count, > + test_app_never_process, > + &app->never_process_count); > + > + TEST_ASSERT(app->never_match_reg_id >= 0, "Unable to register " > + "never-match handler"); > + > + for (i = 0; i < NUM_QUEUES; i++) { > + struct app_queue *app_queue = &app->queues[i]; > + uintptr_t queue_id = app_queue->queue_id; > + int reg_id; > + > + reg_id = rte_dispatcher_register(app->dispatcher, > + match_queue, (void > *)queue_id, > + test_app_process_queue, > + app_queue); > + > + TEST_ASSERT(reg_id >= 0, "Unable to register consumer " > + "callback for queue %d", i); > + > + app_queue->dispatcher_reg_id = reg_id; > + } > + > + app->finalize_reg_id = > + rte_dispatcher_finalize_register(app->dispatcher, > + finalize, > + &app->finalize_count); > + TEST_ASSERT_SUCCESS(app->finalize_reg_id, "Error registering " > + "finalize callback"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_unregister_callback(struct test_app *app, uint8_t queue_id) > +{ > + int reg_id = app->queues[queue_id].dispatcher_reg_id; > + int rc; > + > + if (reg_id < 0) /* unregistered already */ > + return 0; > + > + rc = rte_dispatcher_unregister(app->dispatcher, reg_id); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to unregister consumer " > + "callback for queue %d", queue_id); > + > + app->queues[queue_id].dispatcher_reg_id = -1; > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_unregister_callbacks(struct test_app *app) > +{ > + int i; > + int rc; > + > + if (app->never_match_reg_id >= 0) { > + rc = rte_dispatcher_unregister(app->dispatcher, > + app->never_match_reg_id); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to unregister never-match " > + "handler"); > + app->never_match_reg_id = -1; > + } > + > + for (i = 0; i < NUM_QUEUES; i++) { > + rc = test_app_unregister_callback(app, i); > + RETURN_ON_ERROR(rc); > + } > + > + if (app->finalize_reg_id >= 0) { > + rc = rte_dispatcher_finalize_unregister( > + app->dispatcher, app->finalize_reg_id > + ); > + app->finalize_reg_id = -1; > + } > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_start_dispatcher(struct test_app *app) > +{ > + int rc; > + > + rc = rte_dispatcher_start(app->dispatcher); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to start the event dispatcher"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_stop_dispatcher(struct test_app *app) > +{ > + int rc; > + > + rc = rte_dispatcher_stop(app->dispatcher); > + > + TEST_ASSERT_SUCCESS(rc, "Unable to stop the event dispatcher"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_reset_dispatcher_stats(struct test_app *app) > +{ > + struct rte_dispatcher_stats stats; > + > + rte_dispatcher_stats_reset(app->dispatcher); > + > + memset(&stats, 0xff, sizeof(stats)); > + > + rte_dispatcher_stats_get(app->dispatcher, &stats); > + > + TEST_ASSERT_EQUAL(stats.poll_count, 0, "Poll count not zero"); > + TEST_ASSERT_EQUAL(stats.ev_batch_count, 0, "Batch count not zero"); > + TEST_ASSERT_EQUAL(stats.ev_dispatch_count, 0, "Dispatch count " > + "not zero"); > + TEST_ASSERT_EQUAL(stats.ev_drop_count, 0, "Drop count not zero"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_setup_service_core(struct test_app *app, unsigned int lcore_id) > +{ > + int rc; > + > + rc = rte_service_lcore_add(lcore_id); > + TEST_ASSERT_SUCCESS(rc, "Unable to make lcore %d an event dispatcher " > + "service core", lcore_id); > + > + rc = rte_service_map_lcore_set(app->dispatcher_service_id, lcore_id, > 1); > + TEST_ASSERT_SUCCESS(rc, "Unable to map event dispatcher service"); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_setup_service_cores(struct test_app *app) > +{ > + int i; > + int lcore_id = -1; > + > + for (i = 0; i < NUM_SERVICE_CORES; i++) { > + lcore_id = rte_get_next_lcore(lcore_id, 1, 0); > + > + TEST_ASSERT(lcore_id != RTE_MAX_LCORE, > + "Too few lcores. Needs at least %d worker lcores", > + NUM_SERVICE_CORES); > + > + app->service_lcores[i] = lcore_id; > + } > + > + for (i = 0; i < NUM_SERVICE_CORES; i++) { > + int rc; > + > + rc = test_app_setup_service_core(app, app->service_lcores[i]); > + > + RETURN_ON_ERROR(rc); > + } > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_teardown_service_core(struct test_app *app, unsigned int lcore_id) > +{ > + int rc; > + > + rc = rte_service_map_lcore_set(app->dispatcher_service_id, lcore_id, > 0); > + TEST_ASSERT_SUCCESS(rc, "Unable to unmap event dispatcher service"); > + > + rc = rte_service_lcore_del(lcore_id); > + TEST_ASSERT_SUCCESS(rc, "Unable change role of service lcore %d", > + lcore_id); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_teardown_service_cores(struct test_app *app) > +{ > + int i; > + > + for (i = 0; i < NUM_SERVICE_CORES; i++) { > + unsigned int lcore_id = app->service_lcores[i]; > + int rc; > + > + rc = test_app_teardown_service_core(app, lcore_id); > + > + RETURN_ON_ERROR(rc); > + } > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_start_service_cores(struct test_app *app) > +{ > + int i; > + > + for (i = 0; i < NUM_SERVICE_CORES; i++) { > + unsigned int lcore_id = app->service_lcores[i]; > + int rc; > + > + rc = rte_service_lcore_start(lcore_id); > + TEST_ASSERT_SUCCESS(rc, "Unable to start service lcore %d", > + lcore_id); > + > + RETURN_ON_ERROR(rc); > + } > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_stop_service_cores(struct test_app *app) > +{ > + int i; > + > + for (i = 0; i < NUM_SERVICE_CORES; i++) { > + unsigned int lcore_id = app->service_lcores[i]; > + int rc; > + > + rc = rte_service_lcore_stop(lcore_id); > + TEST_ASSERT_SUCCESS(rc, "Unable to stop service lcore %d", > + lcore_id); > + > + RETURN_ON_ERROR(rc); > + } > + > + return TEST_SUCCESS; > +} > + > +static int > +test_app_start(struct test_app *app) > +{ > + int rc; > + > + rc = test_app_start_event_dev(app); > + RETURN_ON_ERROR(rc); > + > + rc = test_app_start_service_cores(app); > + RETURN_ON_ERROR(rc); > + > + rc = test_app_start_dispatcher(app); > + > + app->running = true; > + > + return rc; > +} > + > +static int > +test_app_stop(struct test_app *app) > +{ > + int rc; > + > + rc = test_app_stop_dispatcher(app); > + RETURN_ON_ERROR(rc); > + > + test_app_stop_service_cores(app); > + RETURN_ON_ERROR(rc); > + > + test_app_stop_event_dev(app); > + RETURN_ON_ERROR(rc); > + > + app->running = false; > + > + return TEST_SUCCESS; > +} > + > +struct test_app *test_app; > + > +static int > +test_setup(void) > +{ > + int rc; > + > + test_app = test_app_create(); > + TEST_ASSERT(test_app != NULL, "Unable to allocate memory"); > + > + rc = test_app_setup_event_dev(test_app); > + RETURN_ON_ERROR(rc); > + > + rc = test_app_create_dispatcher(test_app); > + > + rc = test_app_setup_service_cores(test_app); > + RETURN_ON_ERROR(rc); > + > + rc = test_app_register_callbacks(test_app); > + RETURN_ON_ERROR(rc); > + > + rc = test_app_bind_ports(test_app); > + > + return rc; > +} > + > +static void test_teardown(void) > +{ > + if (test_app->running) > + test_app_stop(test_app); > + > + test_app_teardown_service_cores(test_app); > + > + test_app_unregister_callbacks(test_app); > + > + test_app_unbind_ports(test_app); > + > + test_app_free_dispatcher(test_app); > + > + test_app_teardown_event_dev(test_app); > + > + test_app_free(test_app); > + > + test_app = NULL; > +} > + > +static int > +test_app_get_completed_events(struct test_app *app) > +{ > + return atomic_load_explicit(&app->completed_events, > + memory_order_relaxed); > +} > + > +static int > +test_app_get_errors(struct test_app *app) > +{ > + return atomic_load_explicit(&app->errors, memory_order_relaxed); > +} > + > +static int > +test_basic(void) > +{ > + int rc; > + int i; > + > + rc = test_app_start(test_app); > + RETURN_ON_ERROR(rc); > + > + uint64_t sns[NUM_FLOWS] = { 0 }; > + > + for (i = 0; i < NUM_EVENTS;) { > + struct rte_event events[ENQUEUE_BURST_SIZE]; > + int left; > + int batch_size; > + int j; > + uint16_t n = 0; > + > + batch_size = 1 + rte_rand_max(ENQUEUE_BURST_SIZE); > + left = NUM_EVENTS - i; > + > + batch_size = RTE_MIN(left, batch_size); > + > + for (j = 0; j < batch_size; j++) { > + struct rte_event *event = &events[j]; > + uint64_t sn; > + uint32_t flow_id; > + > + flow_id = rte_rand_max(NUM_FLOWS); > + > + sn = sns[flow_id]++; > + > + *event = (struct rte_event) { > + .queue_id = 0, > + .flow_id = flow_id, > + .sched_type = RTE_SCHED_TYPE_ATOMIC, > + .op = RTE_EVENT_OP_NEW, > + .priority = RTE_EVENT_DEV_PRIORITY_NORMAL, > + .u64 = sn > + }; > + } > + > + while (n < batch_size) > + n += > rte_event_enqueue_new_burst(test_app->event_dev_id, > + DRIVER_PORT_ID, > + events + n, > + batch_size - n); > + > + i += batch_size; > + } > + > + while (test_app_get_completed_events(test_app) != NUM_EVENTS) > + rte_event_maintain(test_app->event_dev_id, DRIVER_PORT_ID, 0); > + > + rc = test_app_get_errors(test_app); > + TEST_ASSERT(rc == 0, "%d errors occurred", rc); > + > + rc = test_app_stop(test_app); > + RETURN_ON_ERROR(rc); > + > + struct rte_dispatcher_stats stats; > + rte_dispatcher_stats_get(test_app->dispatcher, &stats); > + > + TEST_ASSERT_EQUAL(stats.ev_drop_count, 0, "Drop count is not zero"); > + TEST_ASSERT_EQUAL(stats.ev_dispatch_count, NUM_EVENTS * NUM_QUEUES, > + "Invalid dispatch count"); > + TEST_ASSERT(stats.poll_count > 0, "Poll count is zero"); > + > + TEST_ASSERT_EQUAL(test_app->never_process_count.count, 0, > + "Never-match handler's process function has " > + "been called"); > + > + int finalize_count = > + atomic_load_explicit(&test_app->finalize_count.count, > + memory_order_relaxed); > + > + TEST_ASSERT(finalize_count > 0, "Finalize count is zero"); > + TEST_ASSERT(finalize_count <= (int)stats.ev_dispatch_count, > + "Finalize count larger than event count"); > + > + TEST_ASSERT_EQUAL(finalize_count, (int)stats.ev_batch_count, > + "%"PRIu64" batches dequeued, but finalize called %d > " > + "times", stats.ev_batch_count, finalize_count); > + > + /* > + * The event dispatcher should call often-matching match functions > + * more often, and thus this never-matching match function should > + * be called relatively infrequently. > + */ > + TEST_ASSERT(test_app->never_match_count < > + (stats.ev_dispatch_count / 4), > + "Never-matching match function called suspiciously > often"); > + > + rc = test_app_reset_dispatcher_stats(test_app); > + RETURN_ON_ERROR(rc); > + > + return TEST_SUCCESS; > +} > + > +static int > +test_drop(void) > +{ > + int rc; > + uint8_t unhandled_queue; > + struct rte_dispatcher_stats stats; > + > + unhandled_queue = (uint8_t)rte_rand_max(NUM_QUEUES); > + > + rc = test_app_start(test_app); > + RETURN_ON_ERROR(rc); > + > + rc = test_app_unregister_callback(test_app, unhandled_queue); > + RETURN_ON_ERROR(rc); > + > + struct rte_event event = { > + .queue_id = unhandled_queue, > + .flow_id = 0, > + .sched_type = RTE_SCHED_TYPE_ATOMIC, > + .op = RTE_EVENT_OP_NEW, > + .priority = RTE_EVENT_DEV_PRIORITY_NORMAL, > + .u64 = 0 > + }; > + > + do { > + rc = rte_event_enqueue_burst(test_app->event_dev_id, > + DRIVER_PORT_ID, &event, 1); > + } while (rc == 0); > + > + do { > + rte_dispatcher_stats_get(test_app->dispatcher, &stats); > + > + rte_event_maintain(test_app->event_dev_id, DRIVER_PORT_ID, 0); > + } while (stats.ev_drop_count == 0 && stats.ev_dispatch_count == 0); > + > + rc = test_app_stop(test_app); > + RETURN_ON_ERROR(rc); > + > + TEST_ASSERT_EQUAL(stats.ev_drop_count, 1, "Drop count is not one"); > + TEST_ASSERT_EQUAL(stats.ev_dispatch_count, 0, > + "Dispatch count is not zero"); > + TEST_ASSERT(stats.poll_count > 0, "Poll count is zero"); > + > + return TEST_SUCCESS; > +} > + > +#define MORE_THAN_MAX_HANDLERS 1000 > +#define MIN_HANDLERS 32 > + > +static int > +test_many_handler_registrations(void) > +{ > + int rc; > + int num_regs = 0; > + int reg_ids[MORE_THAN_MAX_HANDLERS]; > + int reg_id; > + int i; > + > + rc = test_app_unregister_callbacks(test_app); > + > + RETURN_ON_ERROR(rc); > + > + for (i = 0; i < MORE_THAN_MAX_HANDLERS; i++) { > + reg_id = rte_dispatcher_register(test_app->dispatcher, > + never_match, NULL, > + test_app_never_process, > NULL); > + if (reg_id < 0) > + break; > + > + reg_ids[num_regs++] = reg_id; > + } > + > + TEST_ASSERT_EQUAL(reg_id, -ENOMEM, "Incorrect return code. Expected " > + "%d but was %d", -ENOMEM, reg_id); > + TEST_ASSERT(num_regs >= MIN_HANDLERS, "Registration failed already " > + "after %d handler registrations.", num_regs); > + > + for (i = 0; i < num_regs; i++) { > + rc = rte_dispatcher_unregister(test_app->dispatcher, > + reg_ids[i]); > + TEST_ASSERT_SUCCESS(rc, "Unable to unregister handler %d", > + reg_ids[i]); > + } > + > + return TEST_SUCCESS; > +} > + > +static void > +dummy_finalize(uint8_t event_dev_id __rte_unused, > + uint8_t event_port_id __rte_unused, > + void *cb_data __rte_unused) > +{ > +} > + > +#define MORE_THAN_MAX_FINALIZERS 1000 > +#define MIN_FINALIZERS 16 > + > +static int > +test_many_finalize_registrations(void) > +{ > + int rc; > + int num_regs = 0; > + int reg_ids[MORE_THAN_MAX_FINALIZERS]; > + int reg_id; > + int i; > + > + rc = test_app_unregister_callbacks(test_app); > + > + RETURN_ON_ERROR(rc); > + > + for (i = 0; i < MORE_THAN_MAX_FINALIZERS; i++) { > + reg_id = rte_dispatcher_finalize_register( > + test_app->dispatcher, dummy_finalize, NULL > + ); > + > + if (reg_id < 0) > + break; > + > + reg_ids[num_regs++] = reg_id; > + } > + > + TEST_ASSERT_EQUAL(reg_id, -ENOMEM, "Incorrect return code. Expected " > + "%d but was %d", -ENOMEM, reg_id); > + TEST_ASSERT(num_regs >= MIN_FINALIZERS, "Finalize registration failed > " > + "already after %d registrations.", num_regs); > + > + for (i = 0; i < num_regs; i++) { > + rc = rte_dispatcher_finalize_unregister( > + test_app->dispatcher, reg_ids[i] > + ); > + TEST_ASSERT_SUCCESS(rc, "Unable to unregister finalizer %d", > + reg_ids[i]); > + } > + > + return TEST_SUCCESS; > +} > + > +static struct unit_test_suite test_suite = { > + .suite_name = "Event dispatcher test suite", > + .unit_test_cases = { > + TEST_CASE_ST(test_setup, test_teardown, test_basic), > + TEST_CASE_ST(test_setup, test_teardown, test_drop), > + TEST_CASE_ST(test_setup, test_teardown, > + test_many_handler_registrations), > + TEST_CASE_ST(test_setup, test_teardown, > + test_many_finalize_registrations), > + TEST_CASES_END() > + } > +}; > + > +static int > +test_dispatcher(void) > +{ > + return unit_test_suite_runner(&test_suite); > +} > + > +REGISTER_TEST_COMMAND(dispatcher_autotest, test_dispatcher); We have new macros (see REGISTER_FAST_TEST for example) so a test is associated to an existing testsuite. I think this test should be part of the fast-test testsuite, wdyt? -- David Marchand