#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <inttypes.h>
#include <odp/helper/odph_api.h>
#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
#define SHM_PKT_POOL_SIZE (512*2048)
#define SHM_PKT_POOL_BUF_SIZE 1856
#define MAX_PKT_BURST 16
#define APPL_MODE_PKT_BURST 0
#define APPL_MODE_PKT_QUEUE 1
#define APPL_MODE_PKT_SCHED 2
#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x))
#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
strrchr((file_name), '/') + 1 : (file_name))
typedef struct {
unsigned int cpu_count;
int if_count;
char **if_names;
int mode;
char *if_str;
double time;
} appl_args_t;
typedef struct {
char *pktio_dev;
int mode;
} thread_args_t;
typedef struct {
appl_args_t appl;
thread_args_t thread[MAX_WORKERS];
} args_t;
static args_t *args;
static int drop_err_pkts(
odp_packet_t pkt_tbl[],
unsigned len);
static void swap_pkt_addrs(
odp_packet_t pkt_tbl[],
unsigned len);
static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
static void print_info(char *progname, appl_args_t *appl_args);
static void usage(char *progname);
{
int ret;
switch (mode) {
case APPL_MODE_PKT_BURST:
break;
case APPL_MODE_PKT_QUEUE:
break;
case APPL_MODE_PKT_SCHED:
break;
default:
ODPH_ABORT("invalid mode %d\n", mode);
}
ODPH_ABORT("Error: pktio create failed for %s\n", dev);
if (mode == APPL_MODE_PKT_SCHED)
ODPH_ABORT("Error: pktin config failed for %s\n", dev);
ODPH_ABORT("Error: pktout config failed for %s\n", dev);
if (ret != 0)
ODPH_ABORT("Error: unable to start %s\n", dev);
printf(" created pktio:%02" PRIu64
", dev:%s, queue mode (ATOMIC queues)\n"
" \tdefault pktio%02" PRIu64 "\n",
return pktio;
}
static int pktio_queue_thread(void *arg)
{
int thr;
thread_args_t *thr_args;
unsigned long pkt_cnt = 0;
unsigned long err_cnt = 0;
thr_args = arg;
ODPH_ERR(" [%02i] Error: lookup of pktio %s failed\n",
thr, thr_args->pktio_dev);
return -1;
}
printf(" [%02i] looked up pktio:%02" PRIu64
", queue mode (ATOMIC queues)\n"
" default pktio%02" PRIu64 "\n",
if ((thr_args->mode == APPL_MODE_PKT_QUEUE) &&
ODPH_ERR(" [%02i] Error: no input queue for %s\n",
thr, thr_args->pktio_dev);
return -1;
}
else
continue;
continue;
ODPH_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
continue;
}
ODPH_ERR(" [%02i] Error: no pktout queue\n", thr);
return -1;
}
swap_pkt_addrs(&pkt, 1);
ODPH_ERR(" [%i] Packet send failed.\n", thr);
continue;
}
printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
fflush(NULL);
}
}
return 0;
}
static int pktio_ifburst_thread(void *arg)
{
int thr;
thread_args_t *thr_args;
int pkts, pkts_ok;
unsigned long pkt_cnt = 0;
unsigned long err_cnt = 0;
unsigned long tmp = 0;
thr_args = arg;
ODPH_ERR(" [%02i] Error: lookup of pktio %s failed\n",
thr, thr_args->pktio_dev);
return -1;
}
printf(" [%02i] looked up pktio:%02" PRIu64 ", burst mode\n",
ODPH_ERR(" [%02i] Error: no pktin queue\n", thr);
return -1;
}
ODPH_ERR(" [%02i] Error: no pktout queue\n", thr);
return -1;
}
if (pkts > 0) {
pkts_ok = drop_err_pkts(pkt_tbl, pkts);
if (pkts_ok > 0) {
int sent;
swap_pkt_addrs(pkt_tbl, pkts_ok);
pkts_ok);
sent = sent > 0 ? sent : 0;
err_cnt += pkts_ok - sent;
do
while (++sent < pkts_ok);
}
}
ODPH_ERR("Dropped frames:%u - err_cnt:%lu\n",
pkts - pkts_ok, ++err_cnt);
tmp += pkts_ok;
((pkt_cnt == 0) && ((tmp-1) < MAX_PKT_BURST)))) {
pkt_cnt += tmp;
printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
fflush(NULL);
tmp = 0;
}
}
}
return 0;
}
int main(int argc, char *argv[])
{
odph_helper_options_t helper_options;
odph_thread_t thread_tbl[MAX_WORKERS];
odph_thread_common_param_t thr_common;
odph_thread_param_t thr_param[MAX_WORKERS];
int num_workers;
int i;
argc = odph_parse_options(argc, argv);
if (odph_options(&helper_options)) {
ODPH_ERR("Error: reading ODP helper options failed.\n");
exit(EXIT_FAILURE);
}
init_param.
mem_model = helper_options.mem_model;
ODPH_ERR("Error: ODP global init failed.\n");
exit(EXIT_FAILURE);
}
ODPH_ERR("Error: ODP local init failed.\n");
exit(EXIT_FAILURE);
}
ODP_CACHE_LINE_SIZE, 0);
ODPH_ERR("Error: shared mem reserve failed.\n");
exit(EXIT_FAILURE);
}
if (args == NULL) {
ODPH_ERR("Error: shared mem alloc failed.\n");
exit(EXIT_FAILURE);
}
memset(args, 0, sizeof(args_t));
args->shm = shm;
parse_args(argc, argv, &args->appl);
print_info(NO_PATH(argv[0]), &args->appl);
num_workers = MAX_WORKERS;
if (args->appl.cpu_count && args->appl.cpu_count < MAX_WORKERS)
num_workers = args->appl.cpu_count;
printf("num worker threads: %i\n", num_workers);
printf("cpu mask: %s\n", cpumaskstr);
params.
pkt.
len = SHM_PKT_POOL_BUF_SIZE;
params.
pkt.
num = SHM_PKT_POOL_SIZE/SHM_PKT_POOL_BUF_SIZE;
ODPH_ERR("Error: packet pool create failed.\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < args->appl.if_count; ++i)
create_pktio(args->appl.if_names[i], pool, args->appl.mode);
odph_thread_common_param_init(&thr_common);
thr_common.instance = instance;
thr_common.cpumask = &cpumask;
for (i = 0; i < num_workers; ++i) {
int (*thr_run_func)(void *);
int if_idx;
if_idx = i % args->appl.if_count;
args->thread[i].pktio_dev = args->appl.if_names[if_idx];
args->thread[i].mode = args->appl.mode;
if (args->appl.mode == APPL_MODE_PKT_BURST)
thr_run_func = pktio_ifburst_thread;
else
thr_run_func = pktio_queue_thread;
odph_thread_param_init(&thr_param[i]);
thr_param[i].start = thr_run_func;
thr_param[i].arg = &args->thread[i];
}
memset(thread_tbl, 0, sizeof(thread_tbl));
odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
if (args->appl.time > 0.0) {
for (i = 0; i < args->appl.if_count; ++i) {
}
}
odph_thread_join(thread_tbl, num_workers);
for (i = 0; i < args->appl.if_count; ++i)
free(args->appl.if_names);
free(args->appl.if_str);
ODPH_ERR("Error: shm free global data\n");
exit(EXIT_FAILURE);
}
}
static int drop_err_pkts(
odp_packet_t pkt_tbl[],
unsigned len)
{
unsigned pkt_cnt = len;
unsigned i, j;
for (i = 0, j = 0; i < len; ++i) {
pkt = pkt_tbl[i];
pkt_cnt--;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
pkt_tbl[j-1] = pkt;
#pragma GCC diagnostic pop
}
}
return pkt_cnt;
}
static void swap_pkt_addrs(
odp_packet_t pkt_tbl[],
unsigned len)
{
odph_ethhdr_t *eth;
odph_ethaddr_t tmp_addr;
odph_ipv4hdr_t *ip;
unsigned i;
for (i = 0; i < len; ++i) {
pkt = pkt_tbl[i];
tmp_addr = eth->dst;
eth->dst = eth->src;
eth->src = tmp_addr;
ip = (odph_ipv4hdr_t *)
ip_tmp_addr = ip->src_addr;
ip->src_addr = ip->dst_addr;
ip->dst_addr = ip_tmp_addr;
}
}
}
}
static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
{
int opt;
char *token;
size_t len;
int i;
static const struct option longopts[] = {
{"count", required_argument, NULL, 'c'},
{"time", required_argument, NULL, 't'},
{"interface", required_argument, NULL, 'i'},
{"mode", required_argument, NULL, 'm'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
static const char *shortopts = "+c:i:m:t:h";
appl_args->cpu_count = 1;
appl_args->mode = APPL_MODE_PKT_SCHED;
appl_args->time = 0;
while (1) {
opt = getopt_long(argc, argv, shortopts, longopts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'c':
appl_args->cpu_count = atoi(optarg);
break;
case 't':
appl_args->time = atof(optarg);
break;
case 'i':
len = strlen(optarg);
if (len == 0) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
len += 1;
appl_args->if_str = malloc(len);
if (appl_args->if_str == NULL) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
strcpy(appl_args->if_str, optarg);
for (token = strtok(appl_args->if_str, ","), i = 0;
token != NULL;
token = strtok(NULL, ","), i++)
;
appl_args->if_count = i;
if (appl_args->if_count == 0) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
appl_args->if_names =
calloc(appl_args->if_count, sizeof(char *));
strcpy(appl_args->if_str, optarg);
for (token = strtok(appl_args->if_str, ","), i = 0;
token != NULL; token = strtok(NULL, ","), i++) {
appl_args->if_names[i] = token;
}
break;
case 'm':
i = atoi(optarg);
switch (i) {
case 0:
appl_args->mode = APPL_MODE_PKT_BURST;
break;
case 1:
appl_args->mode = APPL_MODE_PKT_QUEUE;
break;
case 2:
appl_args->mode = APPL_MODE_PKT_SCHED;
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
break;
case 'h':
usage(argv[0]);
exit(EXIT_SUCCESS);
break;
default:
break;
}
}
if (appl_args->if_count == 0 || appl_args->mode == -1) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
optind = 1;
}
static void print_info(char *progname, appl_args_t *appl_args)
{
int i;
printf("Running ODP appl: \"%s\"\n"
"-----------------\n"
"IF-count: %i\n"
"Using IFs: ",
progname, appl_args->if_count);
for (i = 0; i < appl_args->if_count; ++i)
printf(" %s", appl_args->if_names[i]);
printf("\n"
"Mode: ");
switch (appl_args->mode) {
case APPL_MODE_PKT_BURST:
PRINT_APPL_MODE(APPL_MODE_PKT_BURST);
break;
case APPL_MODE_PKT_QUEUE:
PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE);
break;
case APPL_MODE_PKT_SCHED:
PRINT_APPL_MODE(APPL_MODE_PKT_SCHED);
break;
}
printf("\n\n");
fflush(NULL);
}
static void usage(char *progname)
{
printf("\n"
"Usage: %s OPTIONS\n"
" E.g. %s -i eth1,eth2,eth3 -m 0\n"
"\n"
"OpenDataPlane example application.\n"
"\n"
"Mandatory OPTIONS:\n"
" -i, --interface Eth interfaces (comma-separated, no spaces)\n"
"\n"
"Optional OPTIONS\n"
" -c, --count <number> CPU count, 0=all available, default=1\n"
" -t, --time <seconds> Number of seconds to run (e.g. 0.1).\n"
" -m, --mode 0: Receive and send directly (no queues)\n"
" 1: Receive and send via queues.\n"
" 2: Receive via scheduler, send via queues.\n"
" -h, --help Display help and exit.\n"
"\n", NO_PATH(progname), NO_PATH(progname)
);
}
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_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
#define odp_unlikely(x)
Branch unlikely taken.
uint32_t odp_u32be_t
unsigned 32bit big endian
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_cpumask_first(const odp_cpumask_t *mask)
Find first set CPU in mask.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
#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.
void odp_pktin_queue_param_init(odp_pktin_queue_param_t *param)
Initialize packet input queue parameters.
void odp_pktio_param_init(odp_pktio_param_t *param)
Initialize pktio params.
int odp_pktio_close(odp_pktio_t pktio)
Close a packet IO interface.
odp_pktio_t odp_pktio_lookup(const char *name)
Return a packet IO handle for an already open device.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
int odp_pktin_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
Event queues for packet input.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
Direct packet input queues.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
Receive packets directly from an interface input queue.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
uint64_t odp_pktio_to_u64(odp_pktio_t pktio)
Get printable value for an odp_pktio_t.
int odp_pktin_queue_config(odp_pktio_t pktio, const odp_pktin_queue_param_t *param)
Configure packet input queues.
int odp_pktout_queue_config(odp_pktio_t pktio, const odp_pktout_queue_param_t *param)
Configure packet output queues.
@ ODP_PKTIN_MODE_DIRECT
Direct packet input from the interface.
@ ODP_PKTIN_MODE_QUEUE
Packet input through plain event queues.
@ ODP_PKTIN_MODE_SCHED
Packet input through scheduler and scheduled event queues.
int odp_packet_has_ipv4(odp_packet_t pkt)
Check for IPv4.
int odp_packet_has_eth(odp_packet_t pkt)
Check for Ethernet header.
int odp_packet_has_error(odp_packet_t pkt)
Check for all parse errors in packet.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
void odp_packet_free(odp_packet_t pkt)
Free packet.
void * odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len)
Layer 2 start pointer.
void * odp_packet_l3_ptr(odp_packet_t pkt, uint32_t *len)
Layer 3 start pointer.
int odp_packet_is_valid(odp_packet_t pkt)
Check that packet is valid.
odp_pktio_t odp_packet_input(odp_packet_t pkt)
Packet input interface.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
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()
void odp_pool_print(odp_pool_t pool)
Print pool info.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_event_t odp_queue_deq(odp_queue_t queue)
Dequeue an event from a queue.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
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.
void odp_sys_info_print(void)
Print system info.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Packet input queue parameters.
odp_queue_param_t queue_param
Queue parameters.
odp_pktin_mode_t in_mode
Packet input mode.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
odp_schedule_param_t sched
Scheduler parameters.
odp_schedule_sync_t sync
Synchronization method.