#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <getopt.h>
#include <odp/helper/odph_api.h>
#include <export_results.h>
#define MAX_QUEUES (32 * 1024)
typedef enum {
TEST_MODE_LOOP = 0,
TEST_MODE_PAIR,
} test_mode_t;
typedef struct test_options_t {
uint32_t num_queue;
uint32_t num_event;
uint32_t num_round;
uint32_t max_burst;
uint32_t num_cpu;
test_mode_t mode;
} test_options_t;
typedef struct test_stat_t {
uint64_t rounds;
uint64_t events;
uint64_t nsec;
uint64_t cycles;
uint64_t deq_retry;
uint64_t enq_retry;
} test_stat_t;
typedef struct test_global_t test_global_t;
typedef struct {
test_global_t *global;
test_options_t *options;
test_stat_t stats;
uint32_t src_queue_id[MAX_QUEUES];
uint32_t dst_queue_id[MAX_QUEUES];
uint32_t num_queues;
} thread_args_t;
typedef struct test_global_t {
test_options_t options;
test_common_options_t common_options;
} test_global_t;
static void print_usage(void)
{
printf("\n"
"Plain queue performance test\n"
"\n"
"Usage: odp_queue_perf [options]\n"
"\n"
" -m, --mode <arg> Test mode:\n"
" 0: Loop: events are enqueued back to the same queue they\n"
" were dequeued from (default)\n"
" 1: Pair: queues are paired and events are always moved\n"
" between the queues when doing dequeue/enqueue. Requires\n"
" an even number of both queues and workers.\n"
" -c, --num_cpu Number of worker threads (default 1)\n"
" -q, --num_queue Number of queues (default 1)\n"
" -e, --num_event Number of events per queue (default 1)\n"
" -b, --burst_size Maximum number of events per operation (default 1)\n"
" -p, --private Use separate queues for each worker\n"
" -r, --num_round Number of rounds\n"
" -l, --lockfree Lock-free queues\n"
" -w, --waitfree Wait-free queues\n"
" -s, --single Single producer/consumer queues\n"
" -h, --help This help\n"
"\n");
}
static int parse_options(int argc, char *argv[], test_options_t *test_options)
{
int opt, num_cpu;
int ret = 0;
static const struct option longopts[] = {
{"num_cpu", required_argument, NULL, 'c'},
{"num_queue", required_argument, NULL, 'q'},
{"num_event", required_argument, NULL, 'e'},
{"burst_size", required_argument, NULL, 'b'},
{"mode", required_argument, NULL, 'm'},
{"private", no_argument, NULL, 'p'},
{"num_round", required_argument, NULL, 'r'},
{"lockfree", no_argument, NULL, 'l'},
{"waitfree", no_argument, NULL, 'w'},
{"single", no_argument, NULL, 's'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
static const char *shortopts = "+c:q:e:b:m:pr:lwsh";
test_options->num_cpu = 1;
test_options->num_queue = 1;
test_options->num_event = 1;
test_options->max_burst = 1;
test_options->mode = TEST_MODE_LOOP;
test_options->num_round = 1000;
test_options->single = false;
test_options->private_queues = false;
while (1) {
opt = getopt_long(argc, argv, shortopts, longopts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'c':
test_options->num_cpu = atoi(optarg);
break;
case 'q':
test_options->num_queue = atoi(optarg);
break;
case 'e':
test_options->num_event = atoi(optarg);
break;
case 'b':
test_options->max_burst = atoi(optarg);
break;
case 'm':
if (atoi(optarg) == TEST_MODE_PAIR)
test_options->mode = TEST_MODE_PAIR;
break;
case 'r':
test_options->num_round = atoi(optarg);
break;
case 'l':
break;
case 'w':
break;
case 'p':
test_options->private_queues = true;
break;
case 's':
test_options->single = true;
break;
case 'h':
default:
print_usage();
ret = -1;
break;
}
}
if (test_options->num_queue > MAX_QUEUES || test_options->num_queue == 0) {
ODPH_ERR("Invalid number of queues %u. Test maximum %u.\n",
test_options->num_queue, MAX_QUEUES);
return -1;
}
num_cpu = test_options->num_cpu;
if (num_cpu == 0)
if (test_options->private_queues) {
if ((int)test_options->num_queue < num_cpu) {
ODPH_ERR("Not enough queues for %d workers.\n", num_cpu);
return -1;
}
if (test_options->num_queue % num_cpu)
ODPH_ERR("Warn: %" PRIu32 " queues shared unevenly amongst %" PRIu32 " "
"workers.\n", test_options->num_queue, num_cpu);
}
if (test_options->single && !test_options->private_queues) {
if ((test_options->mode == TEST_MODE_LOOP && num_cpu != 1) ||
(test_options->mode == TEST_MODE_PAIR && num_cpu != 2)) {
ODPH_ERR("Multiple producers/consumers not allowed with single prod/cons queues.\n");
return -1;
}
}
if (test_options->mode == TEST_MODE_PAIR && (test_options->num_queue % 2 || num_cpu % 2)) {
ODPH_ERR("Pair mode requires an even number of queues and workers.\n");
return -1;
}
return ret;
}
static int create_queues(test_global_t *global)
{
uint32_t i, j, max_size, max_num;
test_options_t *test_options = &global->options;
uint32_t num_queue = test_options->num_queue;
uint32_t num_event = test_options->num_event;
uint32_t num_round = test_options->num_round;
uint32_t tot_event = num_queue * num_event;
uint32_t queue_size = test_options->mode == TEST_MODE_PAIR ? 2 * num_event : num_event;
int ret = 0;
printf("\nTesting %s queues\n",
printf(" mode %s\n", test_options->mode == TEST_MODE_LOOP ?
"loop" : "pair");
printf(" private queues %s\n", test_options->private_queues ? "yes" : "no");
printf(" single prod/cons %s\n", test_options->single ? "yes" : "no");
printf(" num rounds %u\n", num_round);
printf(" num queues %u\n", num_queue);
printf(" num events per queue %u\n", num_event);
printf(" queue size %u\n", queue_size);
printf(" max burst size %u\n", test_options->max_burst);
for (i = 0; i < num_queue; i++)
for (i = 0; i < tot_event; i++)
ODPH_ERR("Queue capa failed.\n");
return -1;
}
ODPH_ERR("Pool capa failed.\n");
return -1;
}
ODPH_ERR(
"Max queues supported %u.\n", queue_capa.
plain.
max_num);
return -1;
}
if (max_size && queue_size > max_size) {
ODPH_ERR("Max queue size supported %u.\n", max_size);
return -1;
}
ODPH_ERR("Lockfree queues not supported.\n");
return -1;
}
ODPH_ERR("Max lockfree queues supported %u.\n",
return -1;
}
if (max_size && queue_size > max_size) {
ODPH_ERR("Max lockfree queue size supported %u.\n", max_size);
return -1;
}
ODPH_ERR("Waitfree queues not supported.\n");
return -1;
}
ODPH_ERR("Max waitfree queues supported %u.\n",
return -1;
}
if (max_size && queue_size > max_size) {
ODPH_ERR("Max waitfree queue size supported %u.\n", max_size);
return -1;
}
} else {
ODPH_ERR("Bad queue blocking type.\n");
return -1;
}
if (max_num && tot_event > max_num) {
ODPH_ERR("Max events supported %u.\n", max_num);
return -1;
}
pool_param.
buf.
num = tot_event;
ODPH_ERR("Pool create failed.\n");
return -1;
}
global->pool = pool;
queue_param.
size = queue_size;
if (test_options->single) {
}
for (i = 0; i < num_queue; i++) {
ODPH_ERR("Queue create failed %u.\n", i);
return -1;
}
}
for (i = 0; i < tot_event; i++) {
ODPH_ERR("Event alloc failed %u.\n", i);
ret = -1;
goto free_events;
}
}
for (i = 0; i < num_queue; i++) {
for (j = 0; j < num_event; j++) {
uint32_t id = i * num_event + j;
ODPH_ERR("Queue enq failed %u/%u.\n", i, j);
ret = -1;
goto free_events;
}
}
}
free_events:
for (i = 0; i < tot_event; i++) {
}
if (ret)
ODPH_ERR("Initializing test queues failed.\n");
return ret;
}
static int destroy_queues(test_global_t *global)
{
uint32_t i;
int ret = 0;
test_options_t *test_options = &global->options;
uint32_t num_queue = test_options->num_queue;
for (i = 0; i < num_queue; i++) {
break;
while (1) {
break;
}
ODPH_ERR("Queue destroy failed %u.\n", i);
ret = -1;
break;
}
}
ODPH_ERR("Pool destroy failed.\n");
ret = -1;
}
return ret;
}
static int run_test(void *arg)
{
uint64_t c1, c2, cycles, nsec;
uint32_t rounds;
int num_ev;
thread_args_t *thr_args = arg;
test_global_t *global = thr_args->global;
test_stat_t *stat = &thr_args->stats;
uint64_t num_deq_retry = 0;
uint64_t num_enq_retry = 0;
uint64_t events = 0;
const uint32_t num_queue = thr_args->num_queues;
const uint32_t num_round = thr_args->options->num_round;
const uint32_t num_workers = thr_args->options->num_cpu;
const uint32_t max_burst = thr_args->options->max_burst;
uint32_t queue_idx = 0;
for (uint32_t i = 0; i < num_queue; i++) {
src_queue_tbl[i] = global->queue[thr_args->src_queue_id[i]];
dst_queue_tbl[i] = global->queue[thr_args->dst_queue_id[i]];
}
for (rounds = 0; rounds < num_round; rounds++) {
int num_enq = 0;
do {
src_queue = src_queue_tbl[queue_idx];
dst_queue = dst_queue_tbl[queue_idx];
queue_idx++;
if (queue_idx == num_queue)
queue_idx = 0;
ODPH_ABORT("odp_queue_deq_multi() failed\n");
num_deq_retry++;
} while (num_ev == 0);
while (num_enq < num_ev) {
ODPH_ABORT("odp_queue_enq_multi() failed\n");
num_enq += num;
num_enq_retry++;
}
events += num_ev;
}
while (thr_args->options->mode == TEST_MODE_PAIR &&
int num_enq = 0;
src_queue = src_queue_tbl[queue_idx];
dst_queue = dst_queue_tbl[queue_idx];
queue_idx++;
if (queue_idx == num_queue)
queue_idx = 0;
while (num_enq < num_ev) {
ODPH_ABORT("odp_queue_enq_multi() failed\n");
num_enq += num;
}
}
stat->rounds = rounds;
stat->events = events;
stat->nsec = nsec;
stat->cycles = cycles;
stat->deq_retry = num_deq_retry;
stat->enq_retry = num_enq_retry;
return 0;
}
static void map_queues_to_threads(test_global_t *global)
{
test_options_t *opt = &global->options;
if (opt->mode == TEST_MODE_LOOP) {
if (!opt->private_queues) {
for (uint32_t i = 0; i < opt->num_queue; i++) {
for (uint32_t j = 0; j < opt->num_cpu; j++) {
thread_args_t *thread_args = &global->thread_args[j];
thread_args->src_queue_id[i] = i;
thread_args->dst_queue_id[i] = i;
thread_args->num_queues++;
}
}
return;
}
for (uint32_t i = 0; i < opt->num_queue; i++) {
thread_args_t *thread_args = &global->thread_args[i % opt->num_cpu];
uint32_t queue_idx = thread_args->num_queues;
thread_args->src_queue_id[queue_idx] = i;
thread_args->dst_queue_id[queue_idx] = i;
thread_args->num_queues++;
}
return;
}
if (!opt->private_queues) {
for (uint32_t i = 0; i < opt->num_queue; i += 2) {
for (uint32_t j = 0; j < opt->num_cpu; j++) {
thread_args_t *thread_args = &global->thread_args[j];
uint32_t num_queues = thread_args->num_queues;
if (j % 2 == 0) {
thread_args->src_queue_id[num_queues] = i;
thread_args->dst_queue_id[num_queues] = i + 1;
} else {
thread_args->src_queue_id[num_queues] = i + 1;
thread_args->dst_queue_id[num_queues] = i;
}
thread_args->num_queues++;
}
}
return;
}
for (uint32_t i = 0; i < opt->num_queue; i += 2) {
uint32_t num_queues;
uint32_t thread_a_idx = i % opt->num_cpu;
thread_args_t *thread_a_args = &global->thread_args[thread_a_idx];
thread_args_t *thread_b_args = &global->thread_args[thread_a_idx + 1];
num_queues = thread_a_args->num_queues;
thread_a_args->src_queue_id[num_queues] = i;
thread_a_args->dst_queue_id[num_queues] = i + 1;
thread_a_args->num_queues++;
num_queues = thread_b_args->num_queues;
thread_b_args->src_queue_id[num_queues] = i + 1;
thread_b_args->dst_queue_id[num_queues] = i;
thread_b_args->num_queues++;
}
}
static void print_queue_mappings(test_global_t *global)
{
printf("Worker-queue mappings\n");
printf("---------------------\n");
for (uint32_t i = 0; i < global->options.num_cpu; i++) {
thread_args_t *thread_args = &global->thread_args[i];
uint32_t num_queues = thread_args->num_queues;
printf("Worker %u:\n", i);
printf(" src queue idx:");
for (uint32_t j = 0; j < num_queues; j++)
printf(" %" PRIu32 "", thread_args->src_queue_id[j]);
printf("\n dst queue idx:");
for (uint32_t j = 0; j < num_queues; j++)
printf(" %" PRIu32 "", thread_args->dst_queue_id[j]);
printf("\n\n");
}
}
static void init_thread_args(test_global_t *global)
{
for (uint32_t i = 0; i < global->options.num_cpu; i++) {
thread_args_t *thread_args = &global->thread_args[i];
thread_args->global = global;
thread_args->barrier = &global->barrier;
thread_args->options = &global->options;
}
map_queues_to_threads(global);
print_queue_mappings(global);
}
static int start_workers(test_global_t *global)
{
odph_thread_common_param_t thr_common;
int ret;
test_options_t *test_options = &global->options;
int num_cpu = test_options->num_cpu;
if (num_cpu && ret != num_cpu) {
ODPH_ERR("Too many workers. Max supported %i\n.", ret);
return -1;
}
if (num_cpu == 0) {
num_cpu = ret;
test_options->num_cpu = num_cpu;
}
printf(" num workers %u\n\n", num_cpu);
odph_thread_common_param_init(&thr_common);
thr_common.instance = global->instance;
thr_common.cpumask = &cpumask;
init_thread_args(global);
for (int i = 0; i < num_cpu; i++) {
odph_thread_param_init(&thr_param[i]);
thr_param[i].start = run_test;
thr_param[i].arg = &global->thread_args[i];
}
if (odph_thread_create(global->thread_tbl, &thr_common, thr_param,
num_cpu) != num_cpu)
return -1;
return 0;
}
static int output_results(test_global_t *global)
{
int i, num;
double rounds_ave, events_ave, nsec_ave, cycles_ave;
test_stat_t *stats;
test_options_t *test_options = &global->options;
int num_cpu = test_options->num_cpu;
uint64_t rounds_sum = 0;
uint64_t events_sum = 0;
uint64_t nsec_sum = 0;
uint64_t cycles_sum = 0;
uint64_t deq_retry_sum = 0;
uint64_t enq_retry_sum = 0;
stats = &global->thread_args[i].stats;
rounds_sum += stats->rounds;
events_sum += stats->events;
nsec_sum += stats->nsec;
cycles_sum += stats->cycles;
deq_retry_sum += stats->deq_retry;
enq_retry_sum += stats->enq_retry;
}
if (rounds_sum == 0) {
printf("No results.\n");
return 0;
}
rounds_ave = rounds_sum / num_cpu;
events_ave = events_sum / num_cpu;
nsec_ave = nsec_sum / num_cpu;
cycles_ave = cycles_sum / num_cpu;
num = 0;
printf("RESULTS - per thread (Million events per sec):\n");
printf("----------------------------------------------\n");
printf(" 1 2 3 4 5 6 7 8 9 10");
stats = &global->thread_args[i].stats;
if (stats->rounds) {
if ((num % 10) == 0)
printf("\n ");
printf("%6.1f ", (1000.0 * stats->events) / stats->nsec);
num++;
}
}
printf("\n\n");
printf("RESULTS - per thread average (%i threads):\n", num_cpu);
printf("------------------------------------------\n");
printf(" duration: %.3f msec\n", nsec_ave / 1000000);
printf(" num cycles: %.3f M\n", cycles_ave / 1000000);
printf(" events per dequeue: %.3f\n",
events_ave / rounds_ave);
printf(" cycles per event: %.3f\n",
cycles_ave / events_ave);
printf(" dequeue retries: %" PRIu64 "\n", deq_retry_sum);
printf(" enqueue retries: %" PRIu64 "\n", enq_retry_sum);
printf(" events per sec: %.3f M\n\n",
(1000.0 * events_ave) / nsec_ave);
printf("TOTAL events per sec: %.3f M\n\n",
(1000.0 * events_sum) / nsec_ave);
if (global->common_options.is_export) {
if (test_common_write("cycles per event,events per sec (M),TOTAL events per sec (M),"
"dequeue retries,enqueue retries\n")) {
test_common_write_term();
return -1;
}
if (test_common_write("%f,%f,%f,%" PRIu64 ",%" PRIu64 "\n",
cycles_ave / events_ave,
(1000.0 * events_ave) / nsec_ave,
(1000.0 * events_sum) / nsec_ave,
deq_retry_sum,
enq_retry_sum)) {
test_common_write_term();
return -1;
}
test_common_write_term();
}
return 0;
}
int main(int argc, char **argv)
{
odph_helper_options_t helper_options;
test_global_t *global;
test_common_options_t common_options;
argc = odph_parse_options(argc, argv);
if (odph_options(&helper_options)) {
ODPH_ERR("Reading ODP helper options failed.\n");
exit(EXIT_FAILURE);
}
argc = test_common_parse_options(argc, argv);
if (test_common_options(&common_options)) {
ODPH_ERR("Reading test options failed.\n");
exit(EXIT_FAILURE);
}
ODPH_ERR("Global init failed.\n");
return -1;
}
ODPH_ERR("Local init failed.\n");
return -1;
}
shm =
odp_shm_reserve(
"queue_perf_global",
sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
ODPH_ERR("Shared memory reserve failed.\n");
exit(EXIT_FAILURE);
}
if (global == NULL) {
ODPH_ERR("Shared memory address read failed.\n");
exit(EXIT_FAILURE);
}
memset(global, 0, sizeof(test_global_t));
global->common_options = common_options;
if (parse_options(argc, argv, &global->options))
return -1;
global->instance = instance;
if (create_queues(global))
goto destroy;
if (start_workers(global)) {
ODPH_ERR("Test start failed.\n");
return -1;
}
odph_thread_join(global->thread_tbl, global->options.num_cpu);
if (output_results(global)) {
ODPH_ERR("Outputting results failed.\n");
exit(EXIT_FAILURE);
}
destroy:
if (destroy_queues(global)) {
ODPH_ERR("Destroy queues failed.\n");
return -1;
}
ODPH_ERR("Shared memory free failed.\n");
exit(EXIT_FAILURE);
}
ODPH_ERR("Term local failed.\n");
return -1;
}
ODPH_ERR("Term global failed.\n");
return -1;
}
return 0;
}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_inc_u32(odp_atomic_u32_t *atom)
Increment atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
odp_event_t odp_buffer_to_event(odp_buffer_t buf)
Convert buffer handle to event.
odp_buffer_t odp_buffer_alloc(odp_pool_t pool)
Buffer alloc.
#define odp_unlikely(x)
Branch unlikely taken.
uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
CPU cycle count difference.
uint64_t odp_cpu_cycles(void)
Current CPU cycle count.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
void odp_event_free(odp_event_t event)
Free event.
#define ODP_EVENT_INVALID
Invalid event.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_BUFFER
Buffer pool.
odp_nonblocking_t
Non-blocking level.
int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num)
Enqueue multiple events to a queue.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
int odp_queue_capability(odp_queue_capability_t *capa)
Query queue capabilities.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_event_t odp_queue_deq(odp_queue_t queue)
Dequeue an event from a queue.
int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
Enqueue an event to a queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
int odp_queue_deq_multi(odp_queue_t queue, odp_event_t events[], int num)
Dequeue multiple events from a queue.
@ ODP_NONBLOCKING_WF
Non-blocking and wait-free implementation.
@ ODP_BLOCKING
Blocking implementation.
@ ODP_NONBLOCKING_LF
Non-blocking and lock-free implementation.
@ ODP_QUEUE_TYPE_PLAIN
Plain queue.
@ ODP_QUEUE_OP_MT_UNSAFE
Not multithread safe operation.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
void odp_sys_info_print(void)
Print system info.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
@ ODP_THREAD_WORKER
Worker thread.
odp_time_t odp_time_local_strict(void)
Current local time (strict)
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
struct odp_pool_capability_t::@121 buf
Buffer pool capabilities
uint32_t max_num
Maximum number of buffers of any size.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
struct odp_pool_param_t::@125 buf
Parameters for buffer pools.
struct odp_queue_capability_t::@140::@142 waitfree
Wait-free (ODP_NONBLOCKING_WF) implementation capabilities.
uint32_t max_size
Maximum number of events a plain (ODP_BLOCKING) queue can store simultaneously.
uint32_t max_num
Maximum number of plain (ODP_BLOCKING) queues of the default size.
struct odp_queue_capability_t::@140::@141 lockfree
Lock-free (ODP_NONBLOCKING_LF) implementation capabilities.
struct odp_queue_capability_t::@140 plain
Plain queue capabilities.
odp_queue_op_mode_t enq_mode
Enqueue mode.
odp_queue_type_t type
Queue type.
odp_queue_op_mode_t deq_mode
Dequeue mode.
odp_nonblocking_t nonblocking
Non-blocking level.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
uint32_t schedule
Scheduler APIs, e.g., odp_schedule_xxx()
struct odp_feature_t::@148 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()