diff options
Diffstat (limited to 'example')
-rw-r--r-- | example/Makefile.am | 1 | ||||
-rw-r--r-- | example/Makefile.inc | 10 | ||||
-rw-r--r-- | example/README | 3 | ||||
-rw-r--r-- | example/generator/Makefile.am | 5 | ||||
-rw-r--r-- | example/generator/odp_generator.c | 923 | ||||
-rw-r--r-- | example/l2fwd/Makefile.am | 5 | ||||
-rw-r--r-- | example/l2fwd/odp_l2fwd.c | 637 | ||||
-rw-r--r-- | example/odp_example/Makefile.am | 5 | ||||
-rw-r--r-- | example/odp_example/odp_example.c | 1062 | ||||
-rw-r--r-- | example/packet/Makefile.am | 5 | ||||
-rw-r--r-- | example/packet/odp_pktio.c | 635 | ||||
-rw-r--r-- | example/packet_netmap/Makefile.am | 7 | ||||
-rw-r--r-- | example/packet_netmap/odp_pktio_netmap.c | 557 | ||||
-rw-r--r-- | example/timer/Makefile.am | 5 | ||||
-rw-r--r-- | example/timer/odp_timer_test.c | 329 |
15 files changed, 4189 insertions, 0 deletions
diff --git a/example/Makefile.am b/example/Makefile.am new file mode 100644 index 000000000..01a330578 --- /dev/null +++ b/example/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = generator l2fwd odp_example packet packet_netmap timer diff --git a/example/Makefile.inc b/example/Makefile.inc new file mode 100644 index 000000000..b549001fc --- /dev/null +++ b/example/Makefile.inc @@ -0,0 +1,10 @@ +include $(top_srcdir)/Makefile.inc +LIB = $(top_builddir)/lib +LDADD = $(LIB)/libodp.la +AM_CFLAGS += \ + -I$(srcdir) \ + -I$(top_srcdir)/platform/@with_platform@/include/api \ + -I$(top_srcdir)/platform/linux-generic/include/api \ + -I$(top_srcdir)/include + +AM_LDFLAGS += -L$(LIB) diff --git a/example/README b/example/README new file mode 100644 index 000000000..3f8596981 --- /dev/null +++ b/example/README @@ -0,0 +1,3 @@ +Files in this directory are intended to be part of the ODP documentation. +They should not introduce any Doxygen warnings or errors and will be +part of the generated documentation. diff --git a/example/generator/Makefile.am b/example/generator/Makefile.am new file mode 100644 index 000000000..d3bd5bfb7 --- /dev/null +++ b/example/generator/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/example/Makefile.inc + +bin_PROGRAMS = odp_generator + +dist_odp_generator_SOURCES = odp_generator.c diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c new file mode 100644 index 000000000..e4a72fad8 --- /dev/null +++ b/example/generator/odp_generator.c @@ -0,0 +1,923 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_generator.c ODP loopback demo application + */ + +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/time.h> + +#include <odp.h> +#include <odp_packet_io.h> +#include <helper/odp_linux.h> +#include <helper/odp_packet_helper.h> +#include <helper/odp_eth.h> +#include <helper/odp_ip.h> +#include <helper/odp_udp.h> +#include <helper/odp_icmp.h> + +#define MAX_WORKERS 32 /**< max number of works */ +#define SHM_PKT_POOL_SIZE (512*2048) /**< pkt pool size */ +#define SHM_PKT_POOL_BUF_SIZE 1856 /**< pkt pool buf size */ + +#define APPL_MODE_UDP 0 /**< UDP mode */ +#define APPL_MODE_PING 1 /**< ping mode */ +#define APPL_MODE_RCV 2 /**< receive mode */ + +/** print appl mode */ +#define PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x)) + +/** Get rid of path in filename - only for unix-type paths using '/' */ +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \ + strrchr((file_name), '/') + 1 : (file_name)) +/** + * Parsed command line application arguments + */ +typedef struct { + int core_count; /**< system core count */ + int if_count; /**< Number of interfaces to be used */ + char **if_names; /**< Array of pointers to interface names */ + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ + odp_ethaddr_t srcmac; /**< src mac addr */ + odp_ethaddr_t dstmac; /**< dest mac addr */ + unsigned int srcip; /**< src ip addr */ + unsigned int dstip; /**< dest ip addr */ + int mode; /**< work mode */ + int number; /**< packets number to be sent */ + int payload; /**< data len */ + int timeout; /**< wait time */ + int interval; /**< wait interval ms between sending each packet */ +} appl_args_t; + +/** + * counters +*/ +static struct { + odp_atomic_u64_t seq; /**< ip seq to be send */ + odp_atomic_u64_t ip; /**< ip packets */ + odp_atomic_u64_t udp; /**< udp packets */ + odp_atomic_u64_t icmp; /**< icmp packets */ +} counters; + +/** * Thread specific arguments + */ +typedef struct { + char *pktio_dev; /**< Interface name to use */ + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ + int mode; /**< Thread mode */ +} thread_args_t; + +/** + * Grouping of both parsed CL args and thread specific args - alloc together + */ +typedef struct { + /** Application (parsed) arguments */ + appl_args_t appl; + /** Thread specific arguments */ + thread_args_t thread[MAX_WORKERS]; +} args_t; + +/** Global pointer to args */ +static args_t *args; + +/* helper funcs */ +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); +static int scan_ip(char *buf, unsigned int *paddr); +static int scan_mac(char *in, odp_ethaddr_t *des); +static void tv_sub(struct timeval *recvtime, struct timeval *sendtime); + +/** + * Scan ip + * Parse ip address. + * + * @param buf ip address string xxx.xxx.xxx.xx + * @param paddr ip address for odp_packet + * @return 1 success, 0 failed +*/ +static int scan_ip(char *buf, unsigned int *paddr) +{ + int part1, part2, part3, part4; + char tail = 0; + int field; + + if (buf == NULL) + return 0; + + field = sscanf(buf, "%d . %d . %d . %d %c", + &part1, &part2, &part3, &part4, &tail); + + if (field < 4 || field > 5) { + printf("expect 4 field,get %d/n", field); + return 0; + } + + if (tail != 0) { + printf("ip address mixed with non number/n"); + return 0; + } + + if ((part1 >= 0 && part1 <= 255) && (part2 >= 0 && part2 <= 255) && + (part3 >= 0 && part3 <= 255) && (part4 >= 0 && part4 <= 255)) { + if (paddr) + *paddr = part1 << 24 | part2 << 16 | part3 << 8 | part4; + return 1; + } else { + printf("not good ip %d:%d:%d:%d/n", part1, part2, part3, part4); + } + + return 0; +} + +/** + * Scan mac addr form string + * + * @param in mac string + * @param des mac for odp_packet + * @return 1 success, 0 failed + */ +static int scan_mac(char *in, odp_ethaddr_t *des) +{ + int field; + int i; + unsigned int mac[7]; + + field = sscanf(in, "%2x:%2x:%2x:%2x:%2x:%2x", + &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + + for (i = 0; i < 6; i++) + des->addr[i] = mac[i]; + + if (field != 6) + return 0; + return 1; +} + +/** + * set up an udp packet + * + * @param obuf packet buffer +*/ +static void pack_udp_pkt(odp_buffer_t obuf) +{ + char *buf; + int max; + odp_packet_t pkt; + odp_ethhdr_t *eth; + odp_ipv4hdr_t *ip; + odp_udphdr_t *udp; + unsigned short seq; + + buf = odp_buffer_addr(obuf); + if (buf == NULL) + return; + max = odp_buffer_size(obuf); + if (max <= 0) + return; + + pkt = odp_packet_from_buffer(obuf); + /* ether */ + odp_packet_set_l2_offset(pkt, 0); + eth = (odp_ethhdr_t *)buf; + memcpy((char *)eth->src.addr, args->appl.srcmac.addr, ODP_ETHADDR_LEN); + memcpy((char *)eth->dst.addr, args->appl.dstmac.addr, ODP_ETHADDR_LEN); + eth->type = odp_cpu_to_be_16(ODP_ETHTYPE_IPV4); + /* ip */ + odp_packet_set_l3_offset(pkt, ODP_ETHHDR_LEN); + ip = (odp_ipv4hdr_t *)(buf + ODP_ETHHDR_LEN); + ip->dst_addr = odp_cpu_to_be_32(args->appl.dstip); + ip->src_addr = odp_cpu_to_be_32(args->appl.srcip); + ip->ver_ihl = ODP_IPV4 << 4 | ODP_IPV4HDR_IHL_MIN; + ip->tot_len = odp_cpu_to_be_16(args->appl.payload + ODP_UDPHDR_LEN + + ODP_IPV4HDR_LEN); + ip->proto = ODP_IPPROTO_UDP; + seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xFFFF; + ip->id = odp_cpu_to_be_16(seq); + ip->chksum = 0; + odp_ipv4_csum_update(pkt); + /* udp */ + odp_packet_set_l4_offset(pkt, ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN); + udp = (odp_udphdr_t *)(buf + ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN); + udp->src_port = 0; + udp->dst_port = 0; + udp->length = odp_cpu_to_be_16(args->appl.payload + ODP_UDPHDR_LEN); + udp->chksum = 0; + udp->chksum = odp_cpu_to_be_16(odp_ipv4_udp_chksum(pkt)); + odp_packet_set_len(pkt, args->appl.payload + ODP_UDPHDR_LEN + + ODP_IPV4HDR_LEN + ODP_ETHHDR_LEN); +} + +/** + * Set up an icmp packet + * + * @param obuf packet buffer +*/ +static void pack_icmp_pkt(odp_buffer_t obuf) +{ + char *buf; + int max; + odp_packet_t pkt; + odp_ethhdr_t *eth; + odp_ipv4hdr_t *ip; + odp_icmphdr_t *icmp; + struct timeval tval; + uint8_t *tval_d; + unsigned short seq; + + buf = odp_buffer_addr(obuf); + if (buf == NULL) + return; + max = odp_buffer_size(obuf); + if (max <= 0) + return; + + args->appl.payload = 56; + pkt = odp_packet_from_buffer(obuf); + /* ether */ + odp_packet_set_l2_offset(pkt, 0); + eth = (odp_ethhdr_t *)buf; + memcpy((char *)eth->src.addr, args->appl.srcmac.addr, ODP_ETHADDR_LEN); + memcpy((char *)eth->dst.addr, args->appl.dstmac.addr, ODP_ETHADDR_LEN); + eth->type = odp_cpu_to_be_16(ODP_ETHTYPE_IPV4); + /* ip */ + odp_packet_set_l3_offset(pkt, ODP_ETHHDR_LEN); + ip = (odp_ipv4hdr_t *)(buf + ODP_ETHHDR_LEN); + ip->dst_addr = odp_cpu_to_be_32(args->appl.dstip); + ip->src_addr = odp_cpu_to_be_32(args->appl.srcip); + ip->ver_ihl = ODP_IPV4 << 4 | ODP_IPV4HDR_IHL_MIN; + ip->tot_len = odp_cpu_to_be_16(args->appl.payload + ODP_ICMPHDR_LEN + + ODP_IPV4HDR_LEN); + ip->proto = ODP_IPPROTO_ICMP; + seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xffff; + ip->id = odp_cpu_to_be_16(seq); + ip->chksum = 0; + odp_ipv4_csum_update(pkt); + /* icmp */ + icmp = (odp_icmphdr_t *)(buf + ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN); + icmp->type = ICMP_ECHO; + icmp->code = 0; + icmp->un.echo.id = 0; + icmp->un.echo.sequence = ip->id; + tval_d = (uint8_t *)(buf + ODP_ETHHDR_LEN + ODP_IPV4HDR_LEN + + ODP_ICMPHDR_LEN); + /* TODO This should be changed to use an + * ODP timer API once one exists. */ + gettimeofday(&tval, NULL); + memcpy(tval_d, &tval, sizeof(struct timeval)); + icmp->chksum = 0; + icmp->chksum = odp_chksum(icmp, args->appl.payload + + ODP_ICMPHDR_LEN); + + odp_packet_set_len(pkt, args->appl.payload + ODP_ICMPHDR_LEN + + ODP_IPV4HDR_LEN + ODP_ETHHDR_LEN); +} + +/** + * Packet IO loopback worker thread using ODP queues + * + * @param arg thread arguments of type 'thread_args_t *' + */ + +static void *gen_send_thread(void *arg) +{ + int thr; + odp_pktio_t pktio; + thread_args_t *thr_args; + odp_queue_t outq_def; + odp_pktio_params_t params; + socket_params_t *sock_params = ¶ms.sock_params; + + odp_buffer_t buf; + + thr = odp_thread_id(); + thr_args = arg; + + /* Open a packet IO instance for this thread */ + sock_params->type = 1; + pktio = odp_pktio_open(thr_args->pktio_dev, thr_args->pool, ¶ms); + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" [%02i] Error: pktio create failed\n", thr); + return NULL; + } + + outq_def = odp_pktio_outq_getdef(pktio); + if (outq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" [%02i] Error: def output-Q query\n", thr); + return NULL; + } + + printf(" [%02i] created mode: SEND\n", thr); + for (;;) { + int err; + buf = odp_buffer_alloc(thr_args->pool); + if (!odp_buffer_is_valid(buf)) { + ODP_ERR(" [%2i] alloc_single failed\n", thr); + return NULL; + } + + if (args->appl.mode == APPL_MODE_UDP) + pack_udp_pkt(buf); + else if (args->appl.mode == APPL_MODE_PING) + pack_icmp_pkt(buf); + + err = odp_queue_enq(outq_def, buf); + if (err != 0) { + ODP_ERR(" [%02i] send pkt err!\n", thr); + return NULL; + } + + if (args->appl.interval != 0) { + printf(" [%02i] send pkt no:%ju seq %ju\n", + thr, counters.seq, counters.seq%0xffff); + /* TODO use odp timer */ + usleep(args->appl.interval * 1000); + } else { + /* TODO maybe need a rating control */ + /* flood mode use '\r' instead of '\n' */ + printf(" [%02i] send pkt no:%ju seq %ju\r", + thr, counters.seq, counters.seq%0xffff); + } + if (args->appl.number != -1 && counters.seq + >= (unsigned int)args->appl.number) { + break; + } + } + + /* receive number of reply pks until timeout */ + if (args->appl.mode == APPL_MODE_PING && args->appl.number > 0) { + while (args->appl.timeout >= 0) { + if (counters.icmp >= (unsigned int)args->appl.number) + break; + /* TODO use odp timer */ + sleep(1); + args->appl.timeout--; + } + } + + /* print info */ + if (args->appl.mode == APPL_MODE_UDP) { + printf(" [%02i] total send:%ju\n", thr, counters.seq); + } else if (args->appl.mode == APPL_MODE_PING) { + printf(" [%02i] total send:%ju,total receiver %ju\n", + thr, counters.seq, counters.icmp); + } + return arg; +} + +/** + * Print odp packets + * + * @param thr worker id + * @param pkt_tbl packets to be print + * @param len packet number + */ +static void print_pkts(int thr, odp_packet_t pkt_tbl[], unsigned len) +{ + odp_packet_t pkt; + char *buf; + odp_ipv4hdr_t *ip; + odp_udphdr_t *udp; + odp_icmphdr_t *icmp; + struct timeval tvrecv; + struct timeval tvsend; + double rtt; + unsigned i; + size_t offset; + char msg[1024]; + int rlen; + for (i = 0; i < len; ++i) { + pkt = pkt_tbl[i]; + rlen = 0; + + /* only ip pkts */ + if (!odp_packet_inflag_ipv4(pkt)) + continue; + + odp_atomic_inc_u64(&counters.ip); + rlen += sprintf(msg, "receive Packet proto:IP "); + buf = odp_buffer_addr(odp_buffer_from_packet(pkt)); + ip = (odp_ipv4hdr_t *)(buf + odp_packet_l3_offset(pkt)); + rlen += sprintf(msg + rlen, "id %d ", + odp_be_to_cpu_16(ip->id)); + offset = odp_packet_l4_offset(pkt); + + /* udp */ + if (ip->proto == ODP_IPPROTO_UDP) { + odp_atomic_inc_u64(&counters.udp); + udp = (odp_udphdr_t *)(buf + offset); + rlen += sprintf(msg + rlen, "UDP payload %d ", + odp_be_to_cpu_16(udp->length) - + ODP_UDPHDR_LEN); + } + + /* icmp */ + if (ip->proto == ODP_IPPROTO_ICMP) { + icmp = (odp_icmphdr_t *)(buf + offset); + /* echo reply */ + if (icmp->type == ICMP_ECHOREPLY) { + odp_atomic_inc_u64(&counters.icmp); + memcpy(&tvsend, buf + offset + ODP_ICMPHDR_LEN, + sizeof(struct timeval)); + /* TODO This should be changed to use an + * ODP timer API once one exists. */ + gettimeofday(&tvrecv, NULL); + tv_sub(&tvrecv, &tvsend); + rtt = tvrecv.tv_sec*1000 + tvrecv.tv_usec/1000; + rlen += sprintf(msg + rlen, + "ICMP Echo Reply seq %d time %.1f ", + odp_be_to_cpu_16(icmp->un.echo.sequence) + , rtt); + } else if (icmp->type == ICMP_ECHO) { + rlen += sprintf(msg + rlen, + "Icmp Echo Request"); + } + } + + msg[rlen] = '\0'; + printf(" [%02i] %s\n", thr, msg); + } +} + +/** + * Main receive funtion + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static void *gen_recv_thread(void *arg) +{ + int thr; + odp_pktio_t pktio; + thread_args_t *thr_args; + odp_queue_t inq_def; + odp_pktio_params_t params; + char inq_name[ODP_QUEUE_NAME_LEN]; + odp_queue_param_t qparam; + socket_params_t *sock_params = ¶ms.sock_params; + + odp_packet_t pkt; + odp_buffer_t buf; + + thr = odp_thread_id(); + thr_args = arg; + + /* Open a packet IO instance for this thread */ + sock_params->type = 1; + pktio = odp_pktio_open(thr_args->pktio_dev, thr_args->pool, ¶ms); + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" [%02i] Error: pktio create failed\n", thr); + return NULL; + } + + int ret; + qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC; + qparam.sched.group = ODP_SCHED_GROUP_DEFAULT; + snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio); + inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0'; + inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam); + if (inq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" [%02i] Error: pktio queue creation failed\n", thr); + return NULL; + } + + ret = odp_pktio_inq_setdef(pktio, inq_def); + if (ret != 0) { + ODP_ERR(" [%02i] Error: default input-Q setup\n", thr); + return NULL; + } + + printf(" [%02i] created mode: RECEIVE\n", thr); + for (;;) { + /* Use schedule to get buf from any input queue */ + buf = odp_schedule(NULL, ODP_SCHED_WAIT); + + pkt = odp_packet_from_buffer(buf); + /* Drop packets with errors */ + if (odp_unlikely(odp_packet_error(pkt))) { + odp_packet_free(pkt); + continue; + } + + print_pkts(thr, &pkt, 1); + + odp_packet_free(pkt); + } + + return arg; +} +/** + * ODP packet example main function + */ +int main(int argc, char *argv[]) +{ + odp_linux_pthread_t thread_tbl[MAX_WORKERS]; + odp_buffer_pool_t pool; + int thr_id; + int num_workers; + void *pool_base; + int i; + int first_core; + int core_count; + + /* Init ODP before calling anything else */ + if (odp_init_global()) { + ODP_ERR("Error: ODP global init failed.\n"); + exit(EXIT_FAILURE); + } + + /* init counters */ + odp_atomic_init_u64(&counters.seq); + odp_atomic_init_u64(&counters.ip); + odp_atomic_init_u64(&counters.udp); + odp_atomic_init_u64(&counters.icmp); + + /* Reserve memory for args from shared mem */ + args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE); + if (args == NULL) { + ODP_ERR("Error: shared mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + memset(args, 0, sizeof(*args)); + + /* Parse and store the application arguments */ + parse_args(argc, argv, &args->appl); + + /* Print both system and application information */ + print_info(NO_PATH(argv[0]), &args->appl); + + core_count = odp_sys_core_count(); + num_workers = core_count; + + if (args->appl.core_count) + num_workers = args->appl.core_count; + + if (num_workers > MAX_WORKERS) + num_workers = MAX_WORKERS; + + /* ping mode need two worker */ + if (args->appl.mode == APPL_MODE_PING) + num_workers = 2; + + printf("Num worker threads: %i\n", num_workers); + + /* + * By default core #0 runs Linux kernel background tasks. + * Start mapping thread from core #1 + */ + first_core = 1; + + if (core_count == 1) + first_core = 0; + + printf("First core: %i\n\n", first_core); + + /* Init this thread */ + thr_id = odp_thread_create(0); + odp_init_local(thr_id); + + /* Create packet pool */ + pool_base = odp_shm_reserve("shm_packet_pool", + SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE); + if (pool_base == NULL) { + ODP_ERR("Error: packet pool mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + + pool = odp_buffer_pool_create("packet_pool", pool_base, + SHM_PKT_POOL_SIZE, + SHM_PKT_POOL_BUF_SIZE, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_PACKET); + if (pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Error: packet pool create failed.\n"); + exit(EXIT_FAILURE); + } + odp_buffer_pool_print(pool); + + /* Create and init worker threads */ + memset(thread_tbl, 0, sizeof(thread_tbl)); + + if (args->appl.mode == APPL_MODE_PING) { + args->thread[1].pktio_dev = args->appl.if_names[0]; + args->thread[1].pool = pool; + args->thread[1].mode = args->appl.mode; + odp_linux_pthread_create(thread_tbl, 1, 0, + gen_recv_thread, &args->thread[1]); + + args->thread[0].pktio_dev = args->appl.if_names[0]; + args->thread[0].pool = pool; + args->thread[0].mode = args->appl.mode; + odp_linux_pthread_create(thread_tbl, 1, 0, + gen_send_thread, &args->thread[0]); + + /* only wait send thread to join */ + num_workers = 1; + } else { + for (i = 0; i < num_workers; ++i) { + void *(*thr_run_func) (void *); + int core; + int if_idx; + + core = (first_core + i) % core_count; + + if_idx = i % args->appl.if_count; + + args->thread[i].pktio_dev = args->appl.if_names[if_idx]; + args->thread[i].pool = pool; + args->thread[i].mode = args->appl.mode; + + if (args->appl.mode == APPL_MODE_UDP) { + thr_run_func = gen_send_thread; + } else if (args->appl.mode == APPL_MODE_RCV) { + thr_run_func = gen_recv_thread; + } else { + ODP_ERR("ERR MODE\n"); + exit(EXIT_FAILURE); + } + /* + * Create threads one-by-one instead of all-at-once, + * because each thread might get different arguments. + * Calls odp_thread_create(cpu) for each thread + */ + odp_linux_pthread_create(thread_tbl, 1, + core, thr_run_func, + &args->thread[i]); + } + } + + /* Master thread waits for other threads to exit */ + odp_linux_pthread_join(thread_tbl, num_workers); + printf("Exit\n\n"); + + return 0; +} + + +/** + * Parse and store the command line arguments + * + * @param argc argument count + * @param argv[] argument vector + * @param appl_args Store application arguments here + */ +static void parse_args(int argc, char *argv[], appl_args_t *appl_args) +{ + int opt; + int long_index; + char *names, *str, *token, *save; + size_t len; + int i; + static struct option longopts[] = { + {"interface", required_argument, NULL, 'I'}, + {"workers", required_argument, NULL, 'w'}, + {"srcmac", required_argument, NULL, 'a'}, + {"dstmac", required_argument, NULL, 'b'}, + {"srcip", required_argument, NULL, 'c'}, + {"dstip", required_argument, NULL, 'd'}, + {"packetsize", required_argument, NULL, 's'}, + {"mode", required_argument, NULL, 'm'}, + {"count", required_argument, NULL, 'n'}, + {"timeout", required_argument, NULL, 't'}, + {"interval", required_argument, NULL, 'i'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + appl_args->mode = -1; /* Invalid, must be changed by parsing */ + appl_args->number = -1; + appl_args->payload = 56; + appl_args->timeout = -1; + + while (1) { + opt = getopt_long(argc, argv, "+I:a:b:c:d:s:i:m:n:t:w:h", + longopts, &long_index); + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'w': + appl_args->core_count = atoi(optarg); + break; + /* parse packet-io interface names */ + case 'I': + len = strlen(optarg); + if (len == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + len += 1; /* add room for '\0' */ + + names = malloc(len); + if (names == NULL) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* count the number of tokens separated by ',' */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + } + appl_args->if_count = i; + + if (appl_args->if_count == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* allocate storage for the if names */ + appl_args->if_names = + calloc(appl_args->if_count, sizeof(char *)); + + /* store the if names (reset names string) */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + appl_args->if_names[i] = token; + } + break; + + case 'm': + if (optarg[0] == 'u') { + appl_args->mode = APPL_MODE_UDP; + } else if (optarg[0] == 'p') { + appl_args->mode = APPL_MODE_PING; + } else if (optarg[0] == 'r') { + appl_args->mode = APPL_MODE_RCV; + } else { + ODP_ERR("wrong mode!\n"); + exit(EXIT_FAILURE); + } + break; + + case 'a': + if (scan_mac(optarg, &appl_args->srcmac) != 1) { + ODP_ERR("wrong src mac:%s\n", optarg); + exit(EXIT_FAILURE); + } + break; + + case 'b': + if (scan_mac(optarg, &appl_args->dstmac) != 1) { + ODP_ERR("wrong dst mac:%s\n", optarg); + exit(EXIT_FAILURE); + } + break; + + case 'c': + if (scan_ip(optarg, &appl_args->srcip) != 1) { + ODP_ERR("wrong src ip:%s\n", optarg); + exit(EXIT_FAILURE); + } + break; + + case 'd': + if (scan_ip(optarg, &appl_args->dstip) != 1) { + ODP_ERR("wrong dst ip:%s\n", optarg); + exit(EXIT_FAILURE); + } + break; + + case 's': + appl_args->payload = atoi(optarg); + break; + + case 'n': + appl_args->number = atoi(optarg); + break; + + case 't': + appl_args->timeout = atoi(optarg); + break; + + case 'i': + appl_args->interval = atoi(optarg); + if (appl_args->interval <= 200 && geteuid() != 0) { + ODP_ERR("should be root user\n"); + 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; /* reset 'extern optind' from the getopt lib */ +} + +/** + * Print system and application info + */ +static void print_info(char *progname, appl_args_t *appl_args) +{ + int i; + + printf("\n" + "ODP system info\n" + "---------------\n" + "ODP API version: %s\n" + "CPU model: %s\n" + "CPU freq (hz): %"PRIu64"\n" + "Cache line size: %i\n" + "Core count: %i\n" + "\n", + odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(), + odp_sys_cache_line_size(), odp_sys_core_count()); + + 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: "); + if (appl_args->mode == 0) + PRINT_APPL_MODE(0); + else + PRINT_APPL_MODE(0); + printf("\n\n"); + fflush(NULL); +} + +/** + * Prinf usage information + */ +static void usage(char *progname) +{ + printf("\n" + "Usage: %s OPTIONS\n" + " E.g. %s -I eth1 -r\n" + "\n" + "OpenDataPlane example application.\n" + "\n" + " Work mode:\n" + " 1.send udp packets\n" + " odp_generator -I eth0 --srcmac fe:0f:97:c9:e0:44 --dstmac 32:cb:9b:27:2f:1a --srcip 192.168.0.1 --dstip 192.168.0.2 -m u\n" + " 2.receive udp packets\n" + " odp_generator -I eth0 -m r\n" + " 3.work likes ping\n" + " odp_generator -I eth0 --srcmac fe:0f:97:c9:e0:44 --dstmac 32:cb:9b:27:2f:1a --srcip 192.168.0.1 --dstip 192.168.0.2 -m p\n" + "\n" + "Mandatory OPTIONS:\n" + " -I, --interface Eth interfaces (comma-separated, no spaces)\n" + " -a, --srcmac src mac address\n" + " -b, --dstmac dst mac address\n" + " -c, --srcip src ip address\n" + " -d, --dstip dst ip address\n" + " -s, --packetsize payload length of the packets\n" + " -m, --mode work mode: send udp(u), receive(r), send icmp(p)\n" + " -n, --count the number of packets to be send\n" + " -t, --timeout only for ping mode, wait ICMP reply timeout seconds\n" + " -i, --interval wait interval ms between sending each packet\n" + " default is 1000ms. 0 for flood mode\n" + "\n" + "Optional OPTIONS\n" + " -h, --help Display help and exit.\n" + "\n", NO_PATH(progname), NO_PATH(progname) + ); +} +/** + * calc time period + * + *@param recvtime start time + *@param sendtime end time +*/ +static void tv_sub(struct timeval *recvtime, struct timeval *sendtime) +{ + long sec = recvtime->tv_sec - sendtime->tv_sec; + long usec = recvtime->tv_usec - sendtime->tv_usec; + if (usec >= 0) { + recvtime->tv_sec = sec; + recvtime->tv_usec = usec; + } else { + recvtime->tv_sec = sec - 1; + recvtime->tv_usec = -usec; + } +} diff --git a/example/l2fwd/Makefile.am b/example/l2fwd/Makefile.am new file mode 100644 index 000000000..9b5a7ef04 --- /dev/null +++ b/example/l2fwd/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/example/Makefile.inc + +bin_PROGRAMS = odp_l2fwd + +dist_odp_l2fwd_SOURCES = odp_l2fwd.c diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c new file mode 100644 index 000000000..e331ff262 --- /dev/null +++ b/example/l2fwd/odp_l2fwd.c @@ -0,0 +1,637 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_l2fwd.c ODP basic forwarding application + */ + +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> + +#include <odp.h> +#include <helper/odp_linux.h> +#include <helper/odp_packet_helper.h> +#include <helper/odp_eth.h> +#include <helper/odp_ip.h> + +#define MAX_WORKERS 32 +#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 PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x)) + +/** Get rid of path in filename - only for unix-type paths using '/' */ +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \ + strrchr((file_name), '/') + 1 : (file_name)) +/** + * Parsed command line application arguments + */ +typedef struct { + int core_count; + int if_count; /**< Number of interfaces to be used */ + char **if_names; /**< Array of pointers to interface names */ + int mode; /**< Packet IO mode */ + int type; /**< Packet IO type */ + int fanout; /**< Packet IO fanout */ + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ +} appl_args_t; + +/** + * Thread specific arguments + */ +typedef struct { + char *srcif; /**< Source Interface */ + char *dstif; /**< Dest Interface */ + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ + odp_pktio_t srcpktio; /**< Source pktio handle */ + odp_pktio_t dstpktio; /**< Destination pktio handle */ + int mode; /**< Thread mode */ + int type; /**< Thread i/o type */ + int fanout; /**< Thread i/o fanout */ +} thread_args_t; + +/** + * Grouping of both parsed CL args and thread specific args - alloc together + */ +typedef struct { + /** Application (parsed) arguments */ + appl_args_t appl; + /** Thread specific arguments */ + thread_args_t thread[MAX_WORKERS]; +} args_t; + +/** Global pointer to args */ +static args_t *gbl_args; +static int num_workers; + +/* helper funcs */ +static int drop_err_pkts(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); + +/** + * Burst mode: pktio for each thread will be created with either same or + * different params + * + * @param arg thread arguments of type 'thread_args_t *' + * @param pool is the packet pool from where buffers should be taken + */ +static odp_pktio_t burst_mode_init_params(void *arg, odp_buffer_pool_t pool) +{ + thread_args_t *args; + odp_pktio_params_t params; + socket_params_t *sock_params = ¶ms.sock_params; + odp_pktio_t pktio; + + args = arg; + /* Open a packet IO instance for this thread */ + sock_params->type = args->type; + sock_params->fanout = args->fanout; + pktio = odp_pktio_open(args->srcif, pool, ¶ms); + if (pktio == ODP_PKTIO_INVALID) + ODP_ERR(" Error: pktio create failed"); + + return pktio; +} + +/** + * Queue mode: pktio for each thread will be created with either same or + * different params. Queues are created and attached to the pktio. + * + * @param arg thread arguments of type 'thread_args_t *' + * @param pool is the packet pool from where buffers should be taken + */ +static odp_pktio_t queue_mode_init_params(void *arg, odp_buffer_pool_t pool) +{ + char inq_name[ODP_QUEUE_NAME_LEN]; + odp_queue_param_t qparam; + odp_queue_t inq_def; + int ret; + odp_pktio_t pktio = ODP_PKTIO_INVALID; + + pktio = burst_mode_init_params(arg, pool); + if (pktio == ODP_PKTIO_INVALID) + return pktio; + /* + * Create and set the default INPUT queue associated with the 'pktio' + * resource + */ + qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC; + qparam.sched.group = ODP_SCHED_GROUP_DEFAULT; + snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio); + inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0'; + + inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam); + if (inq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" Error: pktio queue creation failed"); + return ODP_PKTIO_INVALID; + } + + ret = odp_pktio_inq_setdef(pktio, inq_def); + if (ret != 0) { + ODP_ERR(" Error: default input-Q setup"); + return ODP_PKTIO_INVALID; + } + + return pktio; +} + +/** + * Packet IO worker thread using ODP queues + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static void *pktio_queue_thread(void *arg) +{ + int thr, i; + thread_args_t *thr_args; + char dstpktio[MAX_WORKERS+1]; + odp_queue_t outq_def; + odp_packet_t pkt; + odp_buffer_t buf; + unsigned long pkt_cnt = 0; + unsigned long err_cnt = 0; + + thr = odp_thread_id(); + thr_args = arg; + + if (thr_args->srcpktio == 0 || thr_args->dstpktio == 0) { + ODP_ERR("Invalid srcpktio:%d dstpktio:%d\n", + thr_args->srcpktio, thr_args->dstpktio); + return NULL; + } + printf("[%02i] srcif:%s dstif:%s spktio:%02i dpktio:%02i QUEUE mode\n", + thr, thr_args->srcif, thr_args->dstif, thr_args->srcpktio, + thr_args->dstpktio); + + /* Populate an array of destination pktio's in all threads as the + * scheduler can take packets from any input queue + */ + for (i = 0; i < num_workers; i++) + dstpktio[i+1] = gbl_args->thread[i].dstpktio; + + /* Loop packets */ + for (;;) { + odp_pktio_t pktio_tmp; + + /* Use schedule to get buf from any input queue */ + buf = odp_schedule(NULL, ODP_SCHED_WAIT); + + pkt = odp_packet_from_buffer(buf); + /* Drop packets with errors */ + if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) { + ODP_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt); + continue; + } + + pktio_tmp = odp_pktio_get_input(pkt); + outq_def = odp_pktio_outq_getdef(dstpktio[pktio_tmp]); + if (outq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" [%02i] Error: def output-Q query\n", thr); + return NULL; + } + + /* Enqueue the packet for output */ + odp_queue_enq(outq_def, buf); + + /* Print packet counts every once in a while */ + if (odp_unlikely(pkt_cnt++ % 100000 == 0)) { + printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt); + fflush(NULL); + } + } + +/* unreachable */ +} + +/** + * Packet IO worker thread using bursts from/to IO resources + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static void *pktio_ifburst_thread(void *arg) +{ + int thr; + thread_args_t *thr_args; + int pkts, pkts_ok; + odp_packet_t pkt_tbl[MAX_PKT_BURST]; + unsigned long pkt_cnt = 0; + unsigned long err_cnt = 0; + unsigned long tmp = 0; + + thr = odp_thread_id(); + thr_args = arg; + + if (thr_args->srcpktio == 0 || thr_args->dstpktio == 0) { + ODP_ERR("Invalid srcpktio:%d dstpktio:%d\n", + thr_args->srcpktio, thr_args->dstpktio); + return NULL; + } + printf("[%02i] srcif:%s dstif:%s spktio:%02i dpktio:%02i BURST mode\n", + thr, thr_args->srcif, thr_args->dstif, thr_args->srcpktio, + thr_args->dstpktio); + + /* Loop packets */ + for (;;) { + pkts = odp_pktio_recv(thr_args->srcpktio, pkt_tbl, + MAX_PKT_BURST); + if (pkts > 0) { + /* Drop packets with errors */ + pkts_ok = drop_err_pkts(pkt_tbl, pkts); + if (pkts_ok > 0) + odp_pktio_send(thr_args->dstpktio, pkt_tbl, + pkts_ok); + if (odp_unlikely(pkts_ok != pkts)) + ODP_ERR("Dropped frames:%u - err_cnt:%lu\n", + pkts-pkts_ok, ++err_cnt); + + /* Print packet counts every once in a while */ + tmp += pkts_ok; + if (odp_unlikely((tmp >= 100000) || /* OR first print:*/ + ((pkt_cnt == 0) && ((tmp-1) < MAX_PKT_BURST)))) { + pkt_cnt += tmp; + printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt); + fflush(NULL); + tmp = 0; + } + } + } + +/* unreachable */ +} + +/** + * ODP L2 forwarding main function + */ +int main(int argc, char *argv[]) +{ + odp_linux_pthread_t thread_tbl[MAX_WORKERS]; + odp_buffer_pool_t pool; + int thr_id; + void *pool_base; + int i; + int first_core; + int core_count; + odp_pktio_t pktio; + + /* Init ODP before calling anything else */ + if (odp_init_global()) { + ODP_ERR("Error: ODP global init failed.\n"); + exit(EXIT_FAILURE); + } + + /* Reserve memory for args from shared mem */ + gbl_args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE); + if (gbl_args == NULL) { + ODP_ERR("Error: shared mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + memset(gbl_args, 0, sizeof(*gbl_args)); + + /* Parse and store the application arguments */ + parse_args(argc, argv, &gbl_args->appl); + + /* Print both system and application information */ + print_info(NO_PATH(argv[0]), &gbl_args->appl); + + core_count = odp_sys_core_count(); + num_workers = core_count; + + if (gbl_args->appl.core_count) + num_workers = gbl_args->appl.core_count; + + if (num_workers > MAX_WORKERS) + num_workers = MAX_WORKERS; + + printf("Num worker threads: %i\n", num_workers); + + if (num_workers < gbl_args->appl.if_count) { + ODP_ERR("Error: core count %d is less than interface count\n", + num_workers); + exit(EXIT_FAILURE); + } + if (gbl_args->appl.if_count % 2 != 0) { + ODP_ERR("Error: interface count %d is odd in fwd appl.\n", + gbl_args->appl.if_count); + exit(EXIT_FAILURE); + } + /* + * By default core #0 runs Linux kernel background tasks. + * Start mapping thread from core #1 + */ + first_core = 1; + + if (core_count == 1) + first_core = 0; + + printf("First core: %i\n\n", first_core); + + /* Init this thread */ + thr_id = odp_thread_create(0); + odp_init_local(thr_id); + + /* Create packet pool */ + pool_base = odp_shm_reserve("shm_packet_pool", + SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE); + if (pool_base == NULL) { + ODP_ERR("Error: packet pool mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + + pool = odp_buffer_pool_create("packet_pool", pool_base, + SHM_PKT_POOL_SIZE, + SHM_PKT_POOL_BUF_SIZE, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_PACKET); + if (pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Error: packet pool create failed.\n"); + exit(EXIT_FAILURE); + } + odp_buffer_pool_print(pool); + + memset(thread_tbl, 0, sizeof(thread_tbl)); + /* initialize threads params */ + for (i = 0; i < num_workers; ++i) { + int if_idx; + + if_idx = i % gbl_args->appl.if_count; + + gbl_args->thread[i].srcif = gbl_args->appl.if_names[if_idx]; + if (if_idx % 2 == 0) + gbl_args->thread[i].dstif = gbl_args->appl.if_names[if_idx+1]; + else + gbl_args->thread[i].dstif = gbl_args->appl.if_names[if_idx-1]; + gbl_args->thread[i].pool = pool; + gbl_args->thread[i].mode = gbl_args->appl.mode; + gbl_args->thread[i].type = gbl_args->appl.type; + gbl_args->thread[i].fanout = gbl_args->appl.fanout; + + if (gbl_args->appl.mode == APPL_MODE_PKT_BURST) { + pktio = burst_mode_init_params(&gbl_args->thread[i], pool); + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" for thread:%02i\n", i); + exit(EXIT_FAILURE); + } + } else { /* APPL_MODE_PKT_QUEUE */ + pktio = queue_mode_init_params(&gbl_args->thread[i], pool); + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" for thread:%02i\n", i); + exit(EXIT_FAILURE); + } + } + gbl_args->thread[i].srcpktio = pktio; + } + for (i = 0; i < num_workers; ++i) { + if (i % 2 == 0) + gbl_args->thread[i].dstpktio = gbl_args->thread[i+1].srcpktio; + else + gbl_args->thread[i].dstpktio = gbl_args->thread[i-1].srcpktio; + } + /* Create worker threads */ + for (i = 0; i < num_workers; ++i) { + void *(*thr_run_func) (void *); + int core; + + core = (first_core + i) % core_count; + + if (gbl_args->appl.mode == APPL_MODE_PKT_BURST) + thr_run_func = pktio_ifburst_thread; + else /* APPL_MODE_PKT_QUEUE */ + thr_run_func = pktio_queue_thread; + odp_linux_pthread_create(thread_tbl, 1, core, thr_run_func, + &gbl_args->thread[i]); + } + + /* Master thread waits for other threads to exit */ + odp_linux_pthread_join(thread_tbl, num_workers); + + printf("Exit\n\n"); + + return 0; +} + +/** + * Drop packets which input parsing marked as containing errors. + * + * Frees packets with error and modifies pkt_tbl[] to only contain packets with + * no detected errors. + * + * @param pkt_tbl Array of packet + * @param len Length of pkt_tbl[] + * + * @return Number of packets with no detected error + */ +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len) +{ + odp_packet_t pkt; + unsigned pkt_cnt = len; + unsigned i, j; + + for (i = 0, j = 0; i < len; ++i) { + pkt = pkt_tbl[i]; + + if (odp_unlikely(odp_packet_error(pkt))) { + odp_packet_free(pkt); /* Drop */ + pkt_cnt--; + } else if (odp_unlikely(i != j++)) { + pkt_tbl[j-1] = pkt; + } + } + + return pkt_cnt; +} + +/** + * Parse and store the command line arguments + * + * @param argc argument count + * @param argv[] argument vector + * @param appl_args Store application arguments here + */ +static void parse_args(int argc, char *argv[], appl_args_t *appl_args) +{ + int opt; + int long_index; + char *names, *str, *token, *save; + size_t len; + int i; + static struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"interface", required_argument, NULL, 'i'}, /* return 'i' */ + {"mode", required_argument, NULL, 'm'}, /* return 'm' */ + {"help", no_argument, NULL, 'h'}, /* return 'h' */ + {NULL, 0, NULL, 0} + }; + + appl_args->mode = -1; /* Invalid, must be changed by parsing */ + appl_args->type = 3; /* 3: ODP_PKTIO_TYPE_SOCKET_MMAP */ + appl_args->fanout = 1; /* turn off fanout by default for mmap */ + + while (1) { + opt = getopt_long(argc, argv, "+c:i:m:t:f:h", + longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'c': + appl_args->core_count = atoi(optarg); + break; + /* parse packet-io interface names */ + case 'i': + len = strlen(optarg); + if (len == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + len += 1; /* add room for '\0' */ + + names = malloc(len); + if (names == NULL) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* count the number of tokens separated by ',' */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + } + appl_args->if_count = i; + + if (appl_args->if_count == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* allocate storage for the if names */ + appl_args->if_names = + calloc(appl_args->if_count, sizeof(char *)); + + /* store the if names (reset names string) */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + appl_args->if_names[i] = token; + } + break; + + case 'm': + i = atoi(optarg); + if (i == 0) + appl_args->mode = APPL_MODE_PKT_BURST; + else + appl_args->mode = APPL_MODE_PKT_QUEUE; + break; + + case 't': + appl_args->type = atoi(optarg); + break; + + case 'f': + appl_args->fanout = atoi(optarg); + 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; /* reset 'extern optind' from the getopt lib */ +} + +/** + * Print system and application info + */ +static void print_info(char *progname, appl_args_t *appl_args) +{ + int i; + + printf("\n" + "ODP system info\n" + "---------------\n" + "ODP API version: %s\n" + "CPU model: %s\n" + "CPU freq (hz): %"PRIu64"\n" + "Cache line size: %i\n" + "Core count: %i\n" + "\n", + odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(), + odp_sys_cache_line_size(), odp_sys_core_count()); + + 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: "); + if (appl_args->mode == APPL_MODE_PKT_BURST) + PRINT_APPL_MODE(APPL_MODE_PKT_BURST); + else + PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE); + printf("\n\n"); + fflush(NULL); +} + +/** + * Prinf usage information + */ +static void usage(char *progname) +{ + printf("\n" + "OpenDataPlane L2 forwarding application.\n" + "\n" + "Usage: %s OPTIONS\n" + " E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n" + " In the above example,\n" + " eth0 will send pkts to eth1 and vice versa\n" + " eth2 will send pkts to eth3 and vice versa\n" + "\n" + "Mandatory OPTIONS:\n" + " -i, --interface Eth interfaces (comma-separated, no spaces)\n" + " -m, --mode 0: Burst send&receive packets (no queues)\n" + " 1: Send&receive packets through ODP queues.\n" + " -t, --type 1: ODP_PKTIO_TYPE_SOCKET_BASIC\n" + " 2: ODP_PKTIO_TYPE_SOCKET_MMSG\n" + " 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n" + " 4: ODP_PKTIO_TYPE_NETMAP\n" + " Default: 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n" + " -f, --fanout 0: off 1: on (Default 1: on)\n" + "\n" + "Optional OPTIONS\n" + " -c, --count <number> Core count.\n" + " -h, --help Display help and exit.\n\n" + "\n", NO_PATH(progname), NO_PATH(progname) + ); +} diff --git a/example/odp_example/Makefile.am b/example/odp_example/Makefile.am new file mode 100644 index 000000000..c1b4ed327 --- /dev/null +++ b/example/odp_example/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/example/Makefile.inc + +bin_PROGRAMS = odp_example + +dist_odp_example_SOURCES = odp_example.c diff --git a/example/odp_example/odp_example.c b/example/odp_example/odp_example.c new file mode 100644 index 000000000..be9609356 --- /dev/null +++ b/example/odp_example/odp_example.c @@ -0,0 +1,1062 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_example.c ODP example application + */ + +#include <string.h> +#include <stdlib.h> + +/* ODP main header */ +#include <odp.h> + +/* ODP helper for Linux apps */ +#include <helper/odp_linux.h> + +/* Needs librt*/ +#include <time.h> + +/* GNU lib C */ +#include <getopt.h> + + +#define MAX_WORKERS 32 /**< Max worker threads */ +#define MSG_POOL_SIZE (4*1024*1024) /**< Message pool size */ +#define MAX_ALLOCS 35 /**< Alloc burst size */ +#define QUEUES_PER_PRIO 64 /**< Queue per priority */ +#define QUEUE_ROUNDS (512*1024) /**< Queue test rounds */ +#define ALLOC_ROUNDS (1024*1024) /**< Alloc test rounds */ +#define MULTI_BUFS_MAX 4 /**< Buffer burst size */ +#define TEST_SEC 2 /**< Time test duration in sec */ + +/** Dummy message */ +typedef struct { + int msg_id; /**< Message ID */ + int seq; /**< Sequence number */ +} test_message_t; + +#define MSG_HELLO 1 /**< Hello */ +#define MSG_ACK 2 /**< Ack */ + +/** Test arguments */ +typedef struct { + int core_count; /**< Core count*/ +} test_args_t; + + +/** @private Barrier for test synchronisation */ +static odp_barrier_t test_barrier; + + +/** + * @internal Clear all scheduled queues. Retry to be sure that all + * buffers have been scheduled. + */ +static void clear_sched_queues(void) +{ + odp_buffer_t buf; + + while (1) { + buf = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + + if (buf == ODP_BUFFER_INVALID) + break; + + odp_buffer_free(buf); + } +} + + +static int create_queue(int thr, odp_buffer_pool_t msg_pool, int prio) +{ + char name[] = "sched_XX_00"; + odp_buffer_t buf; + odp_queue_t queue; + + buf = odp_buffer_alloc(msg_pool); + + if (!odp_buffer_is_valid(buf)) { + ODP_ERR(" [%i] msg_pool alloc failed\n", thr); + return -1; + } + + name[6] = '0' + prio/10; + name[7] = '0' + prio - 10*(prio/10); + + queue = odp_queue_lookup(name); + + if (queue == ODP_QUEUE_INVALID) { + ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name); + return -1; + } + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + + return 0; +} + +static int create_queues(int thr, odp_buffer_pool_t msg_pool, int prio) +{ + char name[] = "sched_XX_YY"; + odp_buffer_t buf; + odp_queue_t queue; + int i; + + name[6] = '0' + prio/10; + name[7] = '0' + prio - 10*(prio/10); + + /* Alloc and enqueue a buffer per queue */ + for (i = 0; i < QUEUES_PER_PRIO; i++) { + name[9] = '0' + i/10; + name[10] = '0' + i - 10*(i/10); + + queue = odp_queue_lookup(name); + + if (queue == ODP_QUEUE_INVALID) { + ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name); + return -1; + } + + buf = odp_buffer_alloc(msg_pool); + + if (!odp_buffer_is_valid(buf)) { + ODP_ERR(" [%i] msg_pool alloc failed\n", thr); + return -1; + } + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + return 0; +} + + +/** + * @internal Test single buffer alloc and free + * + * @param thr Thread + * @param pool Buffer pool + * + * @return 0 if successful + */ +static int test_alloc_single(int thr, odp_buffer_pool_t pool) +{ + int i; + odp_buffer_t temp_buf; + uint64_t t1, t2, cycles, ns; + + t1 = odp_time_get_cycles(); + + for (i = 0; i < ALLOC_ROUNDS; i++) { + temp_buf = odp_buffer_alloc(pool); + + if (!odp_buffer_is_valid(temp_buf)) { + ODP_ERR(" [%i] alloc_single failed\n", thr); + return -1; + } + + odp_buffer_free(temp_buf); + } + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + + printf(" [%i] alloc_sng alloc+free %"PRIu64" cycles, %"PRIu64" ns\n", + thr, cycles/ALLOC_ROUNDS, ns/ALLOC_ROUNDS); + + return 0; +} + +/** + * @internal Test multiple buffers alloc and free + * + * @param thr Thread + * @param pool Buffer pool + * + * @return 0 if successful + */ +static int test_alloc_multi(int thr, odp_buffer_pool_t pool) +{ + int i, j; + odp_buffer_t temp_buf[MAX_ALLOCS]; + uint64_t t1, t2, cycles, ns; + + t1 = odp_time_get_cycles(); + + for (i = 0; i < ALLOC_ROUNDS; i++) { + for (j = 0; j < MAX_ALLOCS; j++) { + temp_buf[j] = odp_buffer_alloc(pool); + + if (!odp_buffer_is_valid(temp_buf[j])) { + ODP_ERR(" [%i] alloc_multi failed\n", thr); + return -1; + } + } + + for (; j > 0; j--) + odp_buffer_free(temp_buf[j-1]); + } + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + + printf(" [%i] alloc_multi alloc+free %"PRIu64" cycles, %"PRIu64" ns\n", + thr, cycles/(ALLOC_ROUNDS*MAX_ALLOCS), + ns/(ALLOC_ROUNDS*MAX_ALLOCS)); + + return 0; +} + +/** + * @internal Test queue polling + * + * Enqueue to and dequeue to/from a single shared queue. + * + * @param thr Thread + * @param msg_pool Buffer pool + * + * @return 0 if successful + */ +static int test_poll_queue(int thr, odp_buffer_pool_t msg_pool) +{ + odp_buffer_t buf; + test_message_t *t_msg; + odp_queue_t queue; + uint64_t t1, t2, cycles, ns; + int i; + + /* Alloc test message */ + buf = odp_buffer_alloc(msg_pool); + + if (!odp_buffer_is_valid(buf)) { + ODP_ERR(" [%i] msg_pool alloc failed\n", thr); + return -1; + } + + /* odp_buffer_print(buf); */ + + t_msg = odp_buffer_addr(buf); + t_msg->msg_id = MSG_HELLO; + t_msg->seq = 0; + + queue = odp_queue_lookup("poll_queue"); + + if (queue == ODP_QUEUE_INVALID) { + printf(" [%i] Queue lookup failed.\n", thr); + return -1; + } + + t1 = odp_time_get_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + + buf = odp_queue_deq(queue); + + if (!odp_buffer_is_valid(buf)) { + ODP_ERR(" [%i] Queue empty.\n", thr); + return -1; + } + } + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + + printf(" [%i] poll_queue enq+deq %"PRIu64" cycles, %"PRIu64" ns\n", + thr, cycles/QUEUE_ROUNDS, ns/QUEUE_ROUNDS); + + odp_buffer_free(buf); + return 0; +} + +/** + * @internal Test scheduling of a single queue - with odp_schedule_one() + * + * Enqueue a buffer to the shared queue. Schedule and enqueue the received + * buffer back into the queue. + * + * @param str Test case name string + * @param thr Thread + * @param msg_pool Buffer pool + * @param prio Priority + * + * @return 0 if successful + */ +static int test_schedule_one_single(const char *str, int thr, + odp_buffer_pool_t msg_pool, int prio) +{ + odp_buffer_t buf; + odp_queue_t queue; + uint64_t t1, t2, cycles, ns; + uint32_t i; + uint32_t tot = 0; + + if (create_queue(thr, msg_pool, prio)) + return -1; + + t1 = odp_time_get_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) + odp_schedule_release_atomic(); + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + tot = i; + + odp_barrier_sync(&test_barrier); + clear_sched_queues(); + + if (tot) { + cycles = cycles/tot; + ns = ns/tot; + } else { + cycles = 0; + ns = 0; + } + + printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n", + thr, str, cycles, ns); + + return 0; +} + +/** + * @internal Test scheduling of multiple queues - with odp_schedule_one() + * + * Enqueue a buffer to each queue. Schedule and enqueue the received + * buffer back into the queue it came from. + * + * @param str Test case name string + * @param thr Thread + * @param msg_pool Buffer pool + * @param prio Priority + * + * @return 0 if successful + */ +static int test_schedule_one_many(const char *str, int thr, + odp_buffer_pool_t msg_pool, int prio) +{ + odp_buffer_t buf; + odp_queue_t queue; + uint64_t t1 = 0; + uint64_t t2 = 0; + uint64_t cycles, ns; + uint32_t i; + uint32_t tot = 0; + + if (create_queues(thr, msg_pool, prio)) + return -1; + + /* Start sched-enq loop */ + t1 = odp_time_get_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) + odp_schedule_release_atomic(); + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + tot = i; + + odp_barrier_sync(&test_barrier); + clear_sched_queues(); + + if (tot) { + cycles = cycles/tot; + ns = ns/tot; + } else { + cycles = 0; + ns = 0; + } + + printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n", + thr, str, cycles, ns); + + return 0; +} + +/** + * @internal Test scheduling of a single queue - with odp_schedule() + * + * Enqueue a buffer to the shared queue. Schedule and enqueue the received + * buffer back into the queue. + * + * @param str Test case name string + * @param thr Thread + * @param msg_pool Buffer pool + * @param prio Priority + * + * @return 0 if successful + */ +static int test_schedule_single(const char *str, int thr, + odp_buffer_pool_t msg_pool, int prio) +{ + odp_buffer_t buf; + odp_queue_t queue; + uint64_t t1, t2, cycles, ns; + uint32_t i; + uint32_t tot = 0; + + if (create_queue(thr, msg_pool, prio)) + return -1; + + t1 = odp_time_get_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + buf = odp_schedule(&queue, ODP_SCHED_WAIT); + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + /* Clear possible locally stored buffers */ + odp_schedule_pause(); + + tot = i; + + while (1) { + buf = odp_schedule(&queue, ODP_SCHED_NO_WAIT); + + if (buf == ODP_BUFFER_INVALID) + break; + + tot++; + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + odp_schedule_resume(); + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + + odp_barrier_sync(&test_barrier); + clear_sched_queues(); + + if (tot) { + cycles = cycles/tot; + ns = ns/tot; + } else { + cycles = 0; + ns = 0; + } + + printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n", + thr, str, cycles, ns); + + return 0; +} + + +/** + * @internal Test scheduling of multiple queues - with odp_schedule() + * + * Enqueue a buffer to each queue. Schedule and enqueue the received + * buffer back into the queue it came from. + * + * @param str Test case name string + * @param thr Thread + * @param msg_pool Buffer pool + * @param prio Priority + * + * @return 0 if successful + */ +static int test_schedule_many(const char *str, int thr, + odp_buffer_pool_t msg_pool, int prio) +{ + odp_buffer_t buf; + odp_queue_t queue; + uint64_t t1 = 0; + uint64_t t2 = 0; + uint64_t cycles, ns; + uint32_t i; + uint32_t tot = 0; + + if (create_queues(thr, msg_pool, prio)) + return -1; + + /* Start sched-enq loop */ + t1 = odp_time_get_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + buf = odp_schedule(&queue, ODP_SCHED_WAIT); + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + /* Clear possible locally stored buffers */ + odp_schedule_pause(); + + tot = i; + + while (1) { + buf = odp_schedule(&queue, ODP_SCHED_NO_WAIT); + + if (buf == ODP_BUFFER_INVALID) + break; + + tot++; + + if (odp_queue_enq(queue, buf)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + odp_schedule_resume(); + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + + odp_barrier_sync(&test_barrier); + clear_sched_queues(); + + if (tot) { + cycles = cycles/tot; + ns = ns/tot; + } else { + cycles = 0; + ns = 0; + } + + printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n", + thr, str, cycles, ns); + + return 0; +} + +/** + * @internal Test scheduling of multiple queues with multi_sched and multi_enq + * + * @param str Test case name string + * @param thr Thread + * @param msg_pool Buffer pool + * @param prio Priority + * + * @return 0 if successful + */ +static int test_schedule_multi(const char *str, int thr, + odp_buffer_pool_t msg_pool, int prio) +{ + odp_buffer_t buf[MULTI_BUFS_MAX]; + odp_queue_t queue; + uint64_t t1 = 0; + uint64_t t2 = 0; + uint64_t cycles, ns; + int i, j; + int num; + uint32_t tot = 0; + char name[] = "sched_XX_YY"; + + name[6] = '0' + prio/10; + name[7] = '0' + prio - 10*(prio/10); + + /* Alloc and enqueue a buffer per queue */ + for (i = 0; i < QUEUES_PER_PRIO; i++) { + name[9] = '0' + i/10; + name[10] = '0' + i - 10*(i/10); + + queue = odp_queue_lookup(name); + + if (queue == ODP_QUEUE_INVALID) { + ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name); + return -1; + } + + for (j = 0; j < MULTI_BUFS_MAX; j++) { + buf[j] = odp_buffer_alloc(msg_pool); + + if (!odp_buffer_is_valid(buf[j])) { + ODP_ERR(" [%i] msg_pool alloc failed\n", thr); + return -1; + } + } + + if (odp_queue_enq_multi(queue, buf, MULTI_BUFS_MAX)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + /* Start sched-enq loop */ + t1 = odp_time_get_cycles(); + + for (i = 0; i < QUEUE_ROUNDS; i++) { + num = odp_schedule_multi(&queue, ODP_SCHED_WAIT, buf, + MULTI_BUFS_MAX); + + tot += num; + + if (odp_queue_enq_multi(queue, buf, num)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + /* Clear possible locally stored buffers */ + odp_schedule_pause(); + + while (1) { + num = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT, buf, + MULTI_BUFS_MAX); + + if (num == 0) + break; + + tot += num; + + if (odp_queue_enq_multi(queue, buf, num)) { + ODP_ERR(" [%i] Queue enqueue failed.\n", thr); + return -1; + } + } + + odp_schedule_resume(); + + + t2 = odp_time_get_cycles(); + cycles = odp_time_diff_cycles(t1, t2); + ns = odp_time_cycles_to_ns(cycles); + + odp_barrier_sync(&test_barrier); + clear_sched_queues(); + + if (tot) { + cycles = cycles/tot; + ns = ns/tot; + } else { + cycles = 0; + ns = 0; + } + + printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n", + thr, str, cycles, ns); + + return 0; +} + +/** + * @internal Worker thread + * + * @param arg Arguments + * + * @return NULL on failure + */ +static void *run_thread(void *arg) +{ + int thr; + odp_buffer_pool_t msg_pool; + + thr = odp_thread_id(); + + printf("Thread %i starts on core %i\n", thr, odp_thread_core()); + + /* + * Test barriers back-to-back + */ + odp_barrier_sync(&test_barrier); + odp_barrier_sync(&test_barrier); + odp_barrier_sync(&test_barrier); + odp_barrier_sync(&test_barrier); + + /* + * Find the buffer pool + */ + msg_pool = odp_buffer_pool_lookup("msg_pool"); + + if (msg_pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR(" [%i] msg_pool not found\n", thr); + return NULL; + } + + odp_barrier_sync(&test_barrier); + + if (test_alloc_single(thr, msg_pool)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_alloc_multi(thr, msg_pool)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_poll_queue(thr, msg_pool)) + return NULL; + + /* Low prio */ + + odp_barrier_sync(&test_barrier); + + if (test_schedule_one_single("sched_one_s_lo", thr, msg_pool, + ODP_SCHED_PRIO_LOWEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_single("sched_____s_lo", thr, msg_pool, + ODP_SCHED_PRIO_LOWEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_one_many("sched_one_m_lo", thr, msg_pool, + ODP_SCHED_PRIO_LOWEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_many("sched_____m_lo", thr, msg_pool, + ODP_SCHED_PRIO_LOWEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_multi("sched_multi_lo", thr, msg_pool, + ODP_SCHED_PRIO_LOWEST)) + return NULL; + + /* High prio */ + + odp_barrier_sync(&test_barrier); + + if (test_schedule_one_single("sched_one_s_hi", thr, msg_pool, + ODP_SCHED_PRIO_HIGHEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_single("sched_____s_hi", thr, msg_pool, + ODP_SCHED_PRIO_HIGHEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_one_many("sched_one_m_hi", thr, msg_pool, + ODP_SCHED_PRIO_HIGHEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_many("sched_____m_hi", thr, msg_pool, + ODP_SCHED_PRIO_HIGHEST)) + return NULL; + + odp_barrier_sync(&test_barrier); + + if (test_schedule_multi("sched_multi_hi", thr, msg_pool, + ODP_SCHED_PRIO_HIGHEST)) + return NULL; + + + printf("Thread %i exits\n", thr); + fflush(NULL); + return arg; +} + +/** + * @internal Test cycle counter accuracy + */ +static void test_time(void) +{ + struct timespec tp1, tp2; + uint64_t t1, t2; + uint64_t ns1, ns2, cycles; + double err; + + if (clock_gettime(CLOCK_MONOTONIC, &tp2)) { + ODP_ERR("clock_gettime failed.\n"); + return; + } + + printf("\nTime accuracy test (%i sec)\n", TEST_SEC); + + do { + if (clock_gettime(CLOCK_MONOTONIC, &tp1)) { + ODP_ERR("clock_gettime failed.\n"); + return; + } + + } while (tp1.tv_sec == tp2.tv_sec); + + t1 = odp_time_get_cycles(); + + do { + if (clock_gettime(CLOCK_MONOTONIC, &tp2)) { + ODP_ERR("clock_gettime failed.\n"); + return; + } + + } while ((tp2.tv_sec - tp1.tv_sec) < TEST_SEC); + + t2 = odp_time_get_cycles(); + + ns1 = (tp2.tv_sec - tp1.tv_sec)*1000000000; + + if (tp2.tv_nsec > tp1.tv_nsec) + ns1 += tp2.tv_nsec - tp1.tv_nsec; + else + ns1 -= tp1.tv_nsec - tp2.tv_nsec; + + cycles = odp_time_diff_cycles(t1, t2); + ns2 = odp_time_cycles_to_ns(cycles); + + err = ((double)(ns2) - (double)ns1) / (double)ns1; + + printf("clock_gettime %"PRIu64" ns\n", ns1); + printf("odp_time_get_cycles %"PRIu64" cycles\n", cycles); + printf("odp_time_cycles_to_ns %"PRIu64" ns\n", ns2); + printf("odp get cycle error %f%%\n", err*100.0); + + printf("\n"); +} + +/** + * @internal Print help + */ +static void print_usage(void) +{ + printf("\n\nUsage: ./odp_example [options]\n"); + printf("Options:\n"); + printf(" -c, --count <number> core count, core IDs start from 1\n"); + printf(" -h, --help this help\n"); + printf("\n\n"); +} + +/** + * @internal Parse arguments + * + * @param argc Argument count + * @param argv Argument vector + * @param args Test arguments + */ +static void parse_args(int argc, char *argv[], test_args_t *args) +{ + int opt; + int long_index; + + static struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + while (1) { + opt = getopt_long(argc, argv, "+c:h", longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'c': + args->core_count = atoi(optarg); + break; + + case 'h': + print_usage(); + exit(EXIT_SUCCESS); + break; + + default: + break; + } + } +} + + +/** + * Test main function + */ +int main(int argc, char *argv[]) +{ + odp_linux_pthread_t thread_tbl[MAX_WORKERS]; + test_args_t args; + int thr_id; + int num_workers; + odp_buffer_pool_t pool; + void *pool_base; + odp_queue_t queue; + int i, j; + int prios; + int first_core; + + printf("\nODP example starts\n"); + + memset(&args, 0, sizeof(args)); + parse_args(argc, argv, &args); + + memset(thread_tbl, 0, sizeof(thread_tbl)); + + if (odp_init_global()) { + printf("ODP global init failed.\n"); + return -1; + } + + printf("\n"); + printf("ODP system info\n"); + printf("---------------\n"); + printf("ODP API version: %s\n", odp_version_api_str()); + printf("CPU model: %s\n", odp_sys_cpu_model_str()); + printf("CPU freq (hz): %"PRIu64"\n", odp_sys_cpu_hz()); + printf("Cache line size: %i\n", odp_sys_cache_line_size()); + printf("Max core count: %i\n", odp_sys_core_count()); + + printf("\n"); + + /* A worker thread per core */ + num_workers = odp_sys_core_count(); + + if (args.core_count) + num_workers = args.core_count; + + /* force to max core count */ + if (num_workers > MAX_WORKERS) + num_workers = MAX_WORKERS; + + printf("num worker threads: %i\n", num_workers); + + /* + * By default core #0 runs Linux kernel background tasks. + * Start mapping thread from core #1 + */ + first_core = 1; + + if (odp_sys_core_count() == 1) + first_core = 0; + + printf("first core: %i\n", first_core); + + /* + * Init this thread. It makes also ODP calls when + * setting up resources for worker threads. + */ + thr_id = odp_thread_create(0); + odp_init_local(thr_id); + + /* Test cycle count accuracy */ + test_time(); + + /* + * Create message pool + */ + pool_base = odp_shm_reserve("msg_pool", + MSG_POOL_SIZE, ODP_CACHE_LINE_SIZE); + + pool = odp_buffer_pool_create("msg_pool", pool_base, MSG_POOL_SIZE, + sizeof(test_message_t), + ODP_CACHE_LINE_SIZE, ODP_BUFFER_TYPE_RAW); + + if (pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Pool create failed.\n"); + return -1; + } + + /* odp_buffer_pool_print(pool); */ + + /* + * Create a queue for direct poll test + */ + queue = odp_queue_create("poll_queue", ODP_QUEUE_TYPE_POLL, NULL); + + if (queue == ODP_QUEUE_INVALID) { + ODP_ERR("Poll queue create failed.\n"); + return -1; + } + + /* + * Create queues for schedule test. QUEUES_PER_PRIO per priority. + */ + prios = odp_schedule_num_prio(); + + for (i = 0; i < prios; i++) { + if (i != ODP_SCHED_PRIO_HIGHEST && + i != ODP_SCHED_PRIO_LOWEST) + continue; + + odp_queue_param_t param; + char name[] = "sched_XX_YY"; + + name[6] = '0' + i/10; + name[7] = '0' + i - 10*(i/10); + + param.sched.prio = i; + param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + param.sched.group = ODP_SCHED_GROUP_DEFAULT; + + for (j = 0; j < QUEUES_PER_PRIO; j++) { + name[9] = '0' + j/10; + name[10] = '0' + j - 10*(j/10); + + queue = odp_queue_create(name, ODP_QUEUE_TYPE_SCHED, + ¶m); + + if (queue == ODP_QUEUE_INVALID) { + ODP_ERR("Schedule queue create failed.\n"); + return -1; + } + } + } + + odp_shm_print_all(); + + /* Barrier to sync test case execution */ + odp_barrier_init_count(&test_barrier, num_workers); + + /* Create and launch worker threads */ + odp_linux_pthread_create(thread_tbl, num_workers, first_core, + run_thread, NULL); + + /* Wait for worker threads to exit */ + odp_linux_pthread_join(thread_tbl, num_workers); + + printf("ODP example complete\n\n"); + + return 0; +} diff --git a/example/packet/Makefile.am b/example/packet/Makefile.am new file mode 100644 index 000000000..2d778ddd6 --- /dev/null +++ b/example/packet/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/example/Makefile.inc + +bin_PROGRAMS = odp_pktio + +dist_odp_pktio_SOURCES = odp_pktio.c diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c new file mode 100644 index 000000000..edf8cfd6f --- /dev/null +++ b/example/packet/odp_pktio.c @@ -0,0 +1,635 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_pktio.c ODP basic packet IO loopback test application + */ + +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> + +#include <odp.h> +#include <helper/odp_linux.h> +#include <helper/odp_packet_helper.h> +#include <helper/odp_eth.h> +#include <helper/odp_ip.h> + +#define MAX_WORKERS 32 +#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 PRINT_APPL_MODE(x) printf("%s(%i)\n", #x, (x)) + +/** Get rid of path in filename - only for unix-type paths using '/' */ +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \ + strrchr((file_name), '/') + 1 : (file_name)) +/** + * Parsed command line application arguments + */ +typedef struct { + int core_count; + int if_count; /**< Number of interfaces to be used */ + char **if_names; /**< Array of pointers to interface names */ + int mode; /**< Packet IO mode */ + int type; /**< Packet IO type */ + int fanout; /**< Packet IO fanout */ + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ +} appl_args_t; + +/** + * Thread specific arguments + */ +typedef struct { + char *pktio_dev; /**< Interface name to use */ + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ + int mode; /**< Thread mode */ + int type; /**< Thread i/o type */ + int fanout; /**< Thread i/o fanout */ +} thread_args_t; + +/** + * Grouping of both parsed CL args and thread specific args - alloc together + */ +typedef struct { + /** Application (parsed) arguments */ + appl_args_t appl; + /** Thread specific arguments */ + thread_args_t thread[MAX_WORKERS]; +} args_t; + +/** Global pointer to args */ +static args_t *args; + +/* helper funcs */ +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); + +/** + * Packet IO loopback worker thread using ODP queues + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static void *pktio_queue_thread(void *arg) +{ + int thr; + odp_buffer_pool_t pkt_pool; + odp_pktio_t pktio; + thread_args_t *thr_args; + odp_queue_t outq_def; + odp_queue_t inq_def; + char inq_name[ODP_QUEUE_NAME_LEN]; + odp_queue_param_t qparam; + odp_packet_t pkt; + odp_buffer_t buf; + int ret; + unsigned long pkt_cnt = 0; + unsigned long err_cnt = 0; + odp_pktio_params_t params; + socket_params_t *sock_params = ¶ms.sock_params; + + thr = odp_thread_id(); + thr_args = arg; + + printf("Pktio thread [%02i] starts, pktio_dev:%s\n", thr, + thr_args->pktio_dev); + + /* Lookup the packet pool */ + pkt_pool = odp_buffer_pool_lookup("packet_pool"); + if (pkt_pool == ODP_BUFFER_POOL_INVALID || pkt_pool != thr_args->pool) { + ODP_ERR(" [%02i] Error: pkt_pool not found\n", thr); + return NULL; + } + + /* Open a packet IO instance for this thread */ + sock_params->type = thr_args->type; + sock_params->fanout = thr_args->fanout; + pktio = odp_pktio_open(thr_args->pktio_dev, pkt_pool, ¶ms); + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" [%02i] Error: pktio create failed\n", thr); + return NULL; + } + + /* + * Create and set the default INPUT queue associated with the 'pktio' + * resource + */ + qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC; + qparam.sched.group = ODP_SCHED_GROUP_DEFAULT; + snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio); + inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0'; + + inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam); + if (inq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" [%02i] Error: pktio queue creation failed\n", thr); + return NULL; + } + + ret = odp_pktio_inq_setdef(pktio, inq_def); + if (ret != 0) { + ODP_ERR(" [%02i] Error: default input-Q setup\n", thr); + return NULL; + } + + printf(" [%02i] created pktio:%02i, queue mode (ATOMIC queues)\n" + " default pktio%02i-INPUT queue:%u\n", + thr, pktio, pktio, inq_def); + + /* Loop packets */ + for (;;) { + odp_pktio_t pktio_tmp; + +#if 1 + /* Use schedule to get buf from any input queue */ + buf = odp_schedule(NULL, ODP_SCHED_WAIT); +#else + /* Always dequeue from the same input queue */ + buf = odp_queue_deq(inq_def); + if (!odp_buffer_is_valid(buf)) + continue; +#endif + + pkt = odp_packet_from_buffer(buf); + + /* Drop packets with errors */ + if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) { + ODP_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt); + continue; + } + + pktio_tmp = odp_pktio_get_input(pkt); + outq_def = odp_pktio_outq_getdef(pktio_tmp); + + if (outq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" [%02i] Error: def output-Q query\n", thr); + return NULL; + } + + /* Swap Eth MACs and possibly IP-addrs before sending back */ + swap_pkt_addrs(&pkt, 1); + + /* Enqueue the packet for output */ + odp_queue_enq(outq_def, buf); + + /* Print packet counts every once in a while */ + if (odp_unlikely(pkt_cnt++ % 100000 == 0)) { + printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt); + fflush(NULL); + } + } + +/* unreachable */ +} + +/** + * Packet IO loopback worker thread using bursts from/to IO resources + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static void *pktio_ifburst_thread(void *arg) +{ + int thr; + odp_buffer_pool_t pkt_pool; + odp_pktio_t pktio; + thread_args_t *thr_args; + int pkts, pkts_ok; + odp_packet_t pkt_tbl[MAX_PKT_BURST]; + unsigned long pkt_cnt = 0; + unsigned long err_cnt = 0; + unsigned long tmp = 0; + odp_pktio_params_t params; + socket_params_t *sock_params = ¶ms.sock_params; + + thr = odp_thread_id(); + thr_args = arg; + + printf("Pktio thread [%02i] starts, pktio_dev:%s\n", thr, + thr_args->pktio_dev); + + /* Lookup the packet pool */ + pkt_pool = odp_buffer_pool_lookup("packet_pool"); + if (pkt_pool == ODP_BUFFER_POOL_INVALID || pkt_pool != thr_args->pool) { + ODP_ERR(" [%02i] Error: pkt_pool not found\n", thr); + return NULL; + } + + /* Open a packet IO instance for this thread */ + sock_params->type = thr_args->type; + sock_params->fanout = thr_args->fanout; + pktio = odp_pktio_open(thr_args->pktio_dev, pkt_pool, ¶ms); + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" [%02i] Error: pktio create failed.\n", thr); + return NULL; + } + + printf(" [%02i] created pktio:%02i, burst mode\n", + thr, pktio); + + /* Loop packets */ + for (;;) { + pkts = odp_pktio_recv(pktio, pkt_tbl, MAX_PKT_BURST); + if (pkts > 0) { + /* Drop packets with errors */ + pkts_ok = drop_err_pkts(pkt_tbl, pkts); + if (pkts_ok > 0) { + /* Swap Eth MACs and IP-addrs */ + swap_pkt_addrs(pkt_tbl, pkts_ok); + odp_pktio_send(pktio, pkt_tbl, pkts_ok); + } + + if (odp_unlikely(pkts_ok != pkts)) + ODP_ERR("Dropped frames:%u - err_cnt:%lu\n", + pkts-pkts_ok, ++err_cnt); + + /* Print packet counts every once in a while */ + tmp += pkts_ok; + if (odp_unlikely((tmp >= 100000) || /* OR first print:*/ + ((pkt_cnt == 0) && ((tmp-1) < MAX_PKT_BURST)))) { + pkt_cnt += tmp; + printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt); + fflush(NULL); + tmp = 0; + } + } + } + +/* unreachable */ +} + +/** + * ODP packet example main function + */ +int main(int argc, char *argv[]) +{ + odp_linux_pthread_t thread_tbl[MAX_WORKERS]; + odp_buffer_pool_t pool; + int thr_id; + int num_workers; + void *pool_base; + int i; + int first_core; + int core_count; + + /* Init ODP before calling anything else */ + if (odp_init_global()) { + ODP_ERR("Error: ODP global init failed.\n"); + exit(EXIT_FAILURE); + } + + /* Reserve memory for args from shared mem */ + args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE); + if (args == NULL) { + ODP_ERR("Error: shared mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + memset(args, 0, sizeof(*args)); + + /* Parse and store the application arguments */ + parse_args(argc, argv, &args->appl); + + /* Print both system and application information */ + print_info(NO_PATH(argv[0]), &args->appl); + + core_count = odp_sys_core_count(); + num_workers = core_count; + + if (args->appl.core_count) + num_workers = args->appl.core_count; + + if (num_workers > MAX_WORKERS) + num_workers = MAX_WORKERS; + + printf("Num worker threads: %i\n", num_workers); + + /* + * By default core #0 runs Linux kernel background tasks. + * Start mapping thread from core #1 + */ + first_core = 1; + + if (core_count == 1) + first_core = 0; + + printf("First core: %i\n\n", first_core); + + /* Init this thread */ + thr_id = odp_thread_create(0); + odp_init_local(thr_id); + + /* Create packet pool */ + pool_base = odp_shm_reserve("shm_packet_pool", + SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE); + if (pool_base == NULL) { + ODP_ERR("Error: packet pool mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + + pool = odp_buffer_pool_create("packet_pool", pool_base, + SHM_PKT_POOL_SIZE, + SHM_PKT_POOL_BUF_SIZE, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_PACKET); + if (pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Error: packet pool create failed.\n"); + exit(EXIT_FAILURE); + } + odp_buffer_pool_print(pool); + + /* Create and init worker threads */ + memset(thread_tbl, 0, sizeof(thread_tbl)); + for (i = 0; i < num_workers; ++i) { + void *(*thr_run_func) (void *); + int core; + int if_idx; + + core = (first_core + i) % core_count; + + if_idx = i % args->appl.if_count; + + args->thread[i].pktio_dev = args->appl.if_names[if_idx]; + args->thread[i].pool = pool; + args->thread[i].mode = args->appl.mode; + args->thread[i].type = args->appl.type; + args->thread[i].fanout = args->appl.fanout; + + if (args->appl.mode == APPL_MODE_PKT_BURST) + thr_run_func = pktio_ifburst_thread; + else /* APPL_MODE_PKT_QUEUE */ + thr_run_func = pktio_queue_thread; + /* + * Create threads one-by-one instead of all-at-once, + * because each thread might get different arguments. + * Calls odp_thread_create(cpu) for each thread + */ + odp_linux_pthread_create(thread_tbl, 1, core, thr_run_func, + &args->thread[i]); + } + + /* Master thread waits for other threads to exit */ + odp_linux_pthread_join(thread_tbl, num_workers); + + printf("Exit\n\n"); + + return 0; +} + +/** + * Drop packets which input parsing marked as containing errors. + * + * Frees packets with error and modifies pkt_tbl[] to only contain packets with + * no detected errors. + * + * @param pkt_tbl Array of packet + * @param len Length of pkt_tbl[] + * + * @return Number of packets with no detected error + */ +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len) +{ + odp_packet_t pkt; + unsigned pkt_cnt = len; + unsigned i, j; + + for (i = 0, j = 0; i < len; ++i) { + pkt = pkt_tbl[i]; + + if (odp_unlikely(odp_packet_error(pkt))) { + odp_packet_free(pkt); /* Drop */ + pkt_cnt--; + } else if (odp_unlikely(i != j++)) { + pkt_tbl[j-1] = pkt; + } + } + + return pkt_cnt; +} + +/** + * Swap eth src<->dst and IP src<->dst addresses + * + * @param pkt_tbl Array of packets + * @param len Length of pkt_tbl[] + */ + +static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len) +{ + odp_packet_t pkt; + odp_ethhdr_t *eth; + odp_ethaddr_t tmp_addr; + odp_ipv4hdr_t *ip; + uint32be_t ip_tmp_addr; /* tmp ip addr */ + unsigned i; + + for (i = 0; i < len; ++i) { + pkt = pkt_tbl[i]; + if (odp_packet_inflag_eth(pkt)) { + eth = (odp_ethhdr_t *)odp_packet_l2(pkt); + + tmp_addr = eth->dst; + eth->dst = eth->src; + eth->src = tmp_addr; + + if (odp_packet_inflag_ipv4(pkt)) { + /* IPv4 */ + ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt); + + ip_tmp_addr = ip->src_addr; + ip->src_addr = ip->dst_addr; + ip->dst_addr = ip_tmp_addr; + } + } + } +} + +/** + * Parse and store the command line arguments + * + * @param argc argument count + * @param argv[] argument vector + * @param appl_args Store application arguments here + */ +static void parse_args(int argc, char *argv[], appl_args_t *appl_args) +{ + int opt; + int long_index; + char *names, *str, *token, *save; + size_t len; + int i; + static struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"interface", required_argument, NULL, 'i'}, /* return 'i' */ + {"mode", required_argument, NULL, 'm'}, /* return 'm' */ + {"help", no_argument, NULL, 'h'}, /* return 'h' */ + {NULL, 0, NULL, 0} + }; + + appl_args->mode = -1; /* Invalid, must be changed by parsing */ + appl_args->type = 3; /* 3: ODP_PKTIO_TYPE_SOCKET_MMAP */ + appl_args->fanout = 1; /* turn off fanout by default for mmap */ + + while (1) { + opt = getopt_long(argc, argv, "+c:i:m:t:f:h", + longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'c': + appl_args->core_count = atoi(optarg); + break; + /* parse packet-io interface names */ + case 'i': + len = strlen(optarg); + if (len == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + len += 1; /* add room for '\0' */ + + names = malloc(len); + if (names == NULL) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* count the number of tokens separated by ',' */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + } + appl_args->if_count = i; + + if (appl_args->if_count == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* allocate storage for the if names */ + appl_args->if_names = + calloc(appl_args->if_count, sizeof(char *)); + + /* store the if names (reset names string) */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + appl_args->if_names[i] = token; + } + break; + + case 'm': + i = atoi(optarg); + if (i == 0) + appl_args->mode = APPL_MODE_PKT_BURST; + else + appl_args->mode = APPL_MODE_PKT_QUEUE; + break; + + case 't': + appl_args->type = atoi(optarg); + break; + + case 'f': + appl_args->fanout = atoi(optarg); + 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; /* reset 'extern optind' from the getopt lib */ +} + +/** + * Print system and application info + */ +static void print_info(char *progname, appl_args_t *appl_args) +{ + int i; + + printf("\n" + "ODP system info\n" + "---------------\n" + "ODP API version: %s\n" + "CPU model: %s\n" + "CPU freq (hz): %"PRIu64"\n" + "Cache line size: %i\n" + "Core count: %i\n" + "\n", + odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(), + odp_sys_cache_line_size(), odp_sys_core_count()); + + 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: "); + if (appl_args->mode == APPL_MODE_PKT_BURST) + PRINT_APPL_MODE(APPL_MODE_PKT_BURST); + else + PRINT_APPL_MODE(APPL_MODE_PKT_QUEUE); + printf("\n\n"); + fflush(NULL); +} + +/** + * Prinf usage information + */ +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" + " -m, --mode 0: Burst send&receive packets (no queues)\n" + " 1: Send&receive packets through ODP queues.\n" + " -t, --type 1: ODP_PKTIO_TYPE_SOCKET_BASIC\n" + " 2: ODP_PKTIO_TYPE_SOCKET_MMSG\n" + " 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n" + " 4: ODP_PKTIO_TYPE_NETMAP\n" + " Default: 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n" + " -f, --fanout 0: off 1: on (Default 1: on)\n" + "\n" + "Optional OPTIONS\n" + " -c, --count <number> Core count.\n" + " -h, --help Display help and exit.\n" + "\n", NO_PATH(progname), NO_PATH(progname) + ); +} diff --git a/example/packet_netmap/Makefile.am b/example/packet_netmap/Makefile.am new file mode 100644 index 000000000..6db0f98d5 --- /dev/null +++ b/example/packet_netmap/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/example/Makefile.inc + +if ODP_NETMAP_ENABLED +bin_PROGRAMS = odp_pktio_netmap +endif + +dist_odp_pktio_netmap_SOURCES = odp_pktio_netmap.c diff --git a/example/packet_netmap/odp_pktio_netmap.c b/example/packet_netmap/odp_pktio_netmap.c new file mode 100644 index 000000000..7d33b19b5 --- /dev/null +++ b/example/packet_netmap/odp_pktio_netmap.c @@ -0,0 +1,557 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * ODP basic packet IO loopback test application + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <getopt.h> +#include <unistd.h> + +#include <linux/if_ether.h> +#include <linux/ip.h> +#include <arpa/inet.h> + +#include <odp.h> +#include <helper/odp_linux.h> +#include <helper/odp_packet_helper.h> +#include <helper/odp_eth.h> +#include <helper/odp_ip.h> +#include <helper/odp_packet_helper.h> + +#include <odp_pktio_netmap.h> + +#define MAX_WORKERS 32 +#define MAX_IFS 16 +#define SHM_PKT_POOL_SIZE (512*2048) +#define SHM_PKT_POOL_BUF_SIZE 1856 +#define MAX_PKT_BURST 16 + +#define PKTIO_MODE_SOCK 0 +#define PKTIO_MODE_NETMAP 1 + +/** Get rid of path in filename - only for unix-type paths using '/' */ +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \ + strrchr((file_name), '/') + 1 : (file_name)) + +/** + * Interface parameters obatained from app arguments + */ +typedef struct { + char if_name[32]; + int pktio_mode; /**< Socket mode or netmap mode */ +} if_info_t; + +/** + * Parsed command line application arguments + */ +typedef struct { + int if_count; /**< Number of interfaces to be used */ + if_info_t *ifs; /**< Array of interface config options */ + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ +} appl_args_t; + +/** + * Thread specific arguments + * In this netmap example, there is a thread polling a network interface + * and another thread polling the ring that is used by the software stack + * to send packets to the same network interface. Each of the two threads + * needs to know which is the output queue corresponding to the other thread + * to be able to pass packets between the stack and the nic. This queue is + * defined by bridge_q below. + */ +typedef struct { + odp_pktio_t pktio; + odp_buffer_pool_t pool; /**< Buffer pool for packet IO */ + char *pktio_dev; /**< Interface name to use */ + int netmap_mode; /**< Either poll the hardware rings or the + rings associated with the host stack */ + odp_queue_t bridge_q; /**< Connect the network stack with the NIC */ +} pktio_info_t; + +/** + * Grouping of both parsed CL args and thread specific args - alloc together + */ +typedef struct { + /** Application (parsed) arguments */ + appl_args_t appl; + /** pktio entries: one for SW ring and one for HW ring */ + pktio_info_t pktios[2 * MAX_IFS]; + /** TODO: find a way to associate private data with pktios */ + /** Lookup table: find pktio_info_t based on pktio id */ + pktio_info_t *pktio_lt[ODP_CONFIG_PKTIO_ENTRIES]; +} args_t; + +/** Global pointer to args */ +static args_t *args; + +/* helper funcs */ +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); + +/** + * Packet IO loopback worker thread using ODP queues + * + * @param arg thread arguments of type 'thread_args_t *' + */ +static void *pktio_queue_thread(void *arg) +{ + int thr; + odp_buffer_pool_t pkt_pool; + odp_packet_t pkt; + odp_buffer_t buf; + unsigned long pkt_cnt = 0; + unsigned long err_cnt = 0; + + (void)arg; + + thr = odp_thread_id(); + printf("Pktio thread [%02i] starts\n", thr); + + /* Lookup the packet pool */ + pkt_pool = odp_buffer_pool_lookup("packet_pool"); + if (pkt_pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR(" [%02i] Error: pkt_pool not found\n", thr); + return NULL; + } + + /* Loop packets */ + for (;;) { + odp_pktio_t pktio_tmp; + odp_queue_t outq_def; + pktio_info_t *pktio_info; + + /* Use schedule to get buf from any input queue */ + buf = odp_schedule(NULL, ODP_SCHED_WAIT); + + pkt = odp_packet_from_buffer(buf); + + /* Drop packets with errors */ + if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) { + ODP_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt); + continue; + } + + pktio_tmp = odp_pktio_get_input(pkt); + if (pktio_tmp == ODP_PKTIO_INVALID) { + ODP_ERR("[%02i] Error: invalid pktio\n", thr); + return NULL; + } + + outq_def = odp_pktio_outq_getdef(pktio_tmp); + + if (outq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" [%02i] Error: def output-Q query\n", + thr); + return NULL; + } + + /* Lookup the thread associated with the entry */ + pktio_info = args->pktio_lt[pktio_tmp]; + + /* Send back packets arrived on physical interface */ + if (pktio_info->netmap_mode == ODP_NETMAP_MODE_HW) { + odp_packet_t pkt_copy; + + pkt_copy = odp_packet_alloc(pkt_pool); + + if (odp_packet_copy(pkt_copy, pkt) != 0) { + ODP_ERR("Packet copy failed!\n"); + odp_packet_free(pkt_copy); + } else { + swap_pkt_addrs(&pkt_copy, 1); + odp_queue_enq(outq_def, + odp_buffer_from_packet(pkt_copy)); + } + } + + odp_queue_enq(pktio_info->bridge_q, buf); + + /* Print packet counts every once in a while */ + if (odp_unlikely(pkt_cnt++ % 100000 == 0)) { + printf(" [%02i] pkt_cnt:%lu\n", thr, pkt_cnt); + fflush(NULL); + } + } + +/* unreachable */ +} + +/** + * ODP packet example main function + */ +int main(int argc, char *argv[]) +{ + odp_linux_pthread_t thread_tbl[MAX_WORKERS]; + odp_buffer_pool_t pool; + int thr_id; + int num_workers; + void *pool_base; + int i; + + /* Init ODP before calling anything else */ + if (odp_init_global()) { + ODP_ERR("Error: ODP global init failed.\n"); + exit(EXIT_FAILURE); + } + + /* Reserve memory for args from shared mem */ + args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE); + if (args == NULL) { + ODP_ERR("Error: shared mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + memset(args, 0, sizeof(*args)); + + /* Parse and store the application arguments */ + parse_args(argc, argv, &args->appl); + + /* Print both system and application information */ + print_info(NO_PATH(argv[0]), &args->appl); + + num_workers = odp_sys_core_count(); + if (num_workers > MAX_WORKERS) + num_workers = MAX_WORKERS; + + /* Init this thread */ + thr_id = odp_thread_create(0); + odp_init_local(thr_id); + + /* Create packet pool */ + pool_base = odp_shm_reserve("shm_packet_pool", + SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE); + if (pool_base == NULL) { + ODP_ERR("Error: packet pool mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + + pool = odp_buffer_pool_create("packet_pool", pool_base, + SHM_PKT_POOL_SIZE, + SHM_PKT_POOL_BUF_SIZE, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_PACKET); + if (pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Error: packet pool create failed.\n"); + exit(EXIT_FAILURE); + } + odp_buffer_pool_print(pool); + + for (i = 0; i < 2 * args->appl.if_count; ++i) { + odp_pktio_params_t params; + netmap_params_t *nm_params = ¶ms.nm_params; + char inq_name[ODP_QUEUE_NAME_LEN]; + odp_queue_t inq_def; + odp_queue_param_t qparam; + odp_pktio_t pktio; + int ret; + + /* Create a pktio polling the hardware rings and one that polls + * the software ring associated with the physical interface + */ + + args->pktios[i].pktio_dev = args->appl.ifs[i / 2].if_name; + memset(nm_params, 0, sizeof(*nm_params)); + nm_params->type = ODP_PKTIO_TYPE_NETMAP; + if (i % 2) { + nm_params->netmap_mode = ODP_NETMAP_MODE_SW; + nm_params->ringid = 0; + } else { + nm_params->netmap_mode = ODP_NETMAP_MODE_HW; + nm_params->ringid = 0; + } + pktio = odp_pktio_open(args->pktios[i].pktio_dev, + pool, ¶ms); + /* Open a packet IO instance for this thread */ + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" [%02i] Err: pktio create\n", i); + return -1; + } + + args->pktios[i].pktio = pktio; + args->pktios[i].pool = pool; + args->pktios[i].netmap_mode = nm_params->netmap_mode; + /* Save pktio_info in the lookup table */ + args->pktio_lt[pktio] = &args->pktios[i]; + /* + * Create and set the default INPUT queue associated with the + * 'pktio' resource + */ + qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT; + qparam.sched.sync = ODP_SCHED_SYNC_NONE; + qparam.sched.group = ODP_SCHED_GROUP_DEFAULT; + snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", + (int)pktio); + inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0'; + + inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, + &qparam); + if (inq_def == ODP_QUEUE_INVALID) { + ODP_ERR(" [%02i] Err: pktio q create\n", i); + return -1; + } + + ret = odp_pktio_inq_setdef(pktio, inq_def); + if (ret != 0) { + ODP_ERR(" [%02i] Err: default input-Q setup\n" + , i); + return -1; + } + + printf(" [%02i] created pktio:%02i, queue mode\n" + " default pktio%02i-INPUT queue:%u\n", + i, pktio, pktio, inq_def); + + /* Prepare for bridging: set bridge_q queue ids */ + if (i % 2) { + odp_pktio_t pktio_bridge; + odp_queue_t outq_def; + + pktio_bridge = args->pktios[i-1].pktio; + outq_def = odp_pktio_outq_getdef(pktio_bridge); + args->pktios[i].bridge_q = outq_def; + + pktio_bridge = args->pktios[i].pktio; + outq_def = odp_pktio_outq_getdef(pktio_bridge); + args->pktios[i-1].bridge_q = outq_def; + } + } + + memset(thread_tbl, 0, sizeof(thread_tbl)); + for (i = 0; i < num_workers; ++i) { + + /* + * Create threads one-by-one instead of all-at-once, + * because each thread might get different arguments + */ + odp_linux_pthread_create(thread_tbl, 1, i, pktio_queue_thread, + NULL); + } + + /* Master thread waits for other threads to exit */ + odp_linux_pthread_join(thread_tbl, num_workers); + + printf("Exit\n\n"); + + return 0; +} + +/** + * Drop packets which input parsing marked as containing errors. + * + * Frees packets with error and modifies pkt_tbl[] to only contain packets with + * no detected errors. + * + * @param pkt_tbl Array of packet + * @param len Length of pkt_tbl[] + * + * @return Number of packets with no detected error + */ +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len) +{ + odp_packet_t pkt; + unsigned pkt_cnt = len; + unsigned i, j; + + for (i = 0, j = 0; i < len; ++i) { + pkt = pkt_tbl[i]; + + if (odp_unlikely(odp_packet_error(pkt))) { + odp_packet_free(pkt); /* Drop */ + pkt_cnt--; + } else if (odp_unlikely(i != j++)) { + pkt_tbl[j-1] = pkt; + } + } + + return pkt_cnt; +} + +/** + * Swap eth src<->dst and IP src<->dst addresses + * + * @param pkt_tbl Array of packets + * @param len Length of pkt_tbl[] + */ +static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len) +{ + odp_packet_t pkt; + odp_ethhdr_t *eth; + odp_ethaddr_t tmp_addr; + odp_ipv4hdr_t *ip; + uint32be_t ip_tmp_addr; /* tmp ip addr */ + unsigned i; + + for (i = 0; i < len; ++i) { + pkt = pkt_tbl[i]; + if (odp_packet_inflag_eth(pkt)) { + eth = (odp_ethhdr_t *)odp_packet_l2(pkt); + + tmp_addr = eth->dst; + eth->dst = eth->src; + eth->src = tmp_addr; + + if (odp_packet_inflag_ipv4(pkt)) { + /* IPv4 */ + ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt); + + ip_tmp_addr = ip->src_addr; + ip->src_addr = ip->dst_addr; + ip->dst_addr = ip_tmp_addr; + } + } + } +} + +/** + * Parse and store the command line arguments + * + * @param argc argument count + * @param argv[] argument vector + * @param appl_args Store application arguments here + */ +static void parse_args(int argc, char *argv[], appl_args_t *appl_args) +{ + int opt; + int long_index; + char *names, *str, *token, *save; + size_t len; + int i; + static struct option longopts[] = { + {"interface", required_argument, NULL, 'i'}, /* return 'i' */ + {"help", no_argument, NULL, 'h'}, /* return 'h' */ + {NULL, 0, NULL, 0} + }; + + while (1) { + opt = getopt_long(argc, argv, "+i:h", longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + /* parse packet-io interface names */ + case 'i': + len = strlen(optarg); + if (len == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + len += 1; /* add room for '\0' */ + + names = malloc(len); + if (names == NULL) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* count the number of tokens separated by ',' */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + } + appl_args->if_count = i; + + if (appl_args->if_count == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + /* allocate storage for the if names */ + appl_args->ifs = + calloc(appl_args->if_count, sizeof(if_info_t)); + + /* store the if names (reset names string) */ + strcpy(names, optarg); + for (str = names, i = 0;; str = NULL, i++) { + token = strtok_r(str, ",", &save); + if (token == NULL) + break; + strncpy(appl_args->ifs[i].if_name, token, + sizeof(appl_args->ifs[i].if_name)); + appl_args->ifs[i].pktio_mode = + PKTIO_MODE_NETMAP; + } + break; + + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + break; + + default: + break; + } + } + + if (appl_args->if_count == 0) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + + optind = 1; /* reset 'extern optind' from the getopt lib */ +} + +/** + * Print system and application info + */ +static void print_info(char *progname, appl_args_t *appl_args) +{ + int i; + + printf("\n" + "ODP system info\n" + "---------------\n" + "ODP API version: %s\n" + "CPU model: %s\n" + "CPU freq (hz): %"PRIu64"\n" + "Cache line size: %i\n" + "Core count: %i\n" + "\n", + odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(), + odp_sys_cache_line_size(), odp_sys_core_count() + ); + 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->ifs[i].if_name); + printf("\n" + "Mode: "); + printf("\n\n"); + fflush(NULL); +} + +/** + * Prinf usage information + */ +static void usage(char *progname) +{ + printf("\n" + "Usage: %s OPTIONS\n" + " E.g. %s -i eth1,eth2,eth3\n" + "\n" + "OpenDataPlane example application.\n" + "\n" + "Mandatory OPTIONS:\n" + " -i, --interface Eth interfaces (comma-separated, no spaces)\n" + "\n" + "Optional OPTIONS\n" + " -h, --help Display help and exit.\n" + "\n", NO_PATH(progname), NO_PATH(progname) + ); +} diff --git a/example/timer/Makefile.am b/example/timer/Makefile.am new file mode 100644 index 000000000..09253a6f0 --- /dev/null +++ b/example/timer/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/example/Makefile.inc + +bin_PROGRAMS = odp_timer_test + +dist_odp_timer_test_SOURCES = odp_timer_test.c diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c new file mode 100644 index 000000000..dbe0e5bed --- /dev/null +++ b/example/timer/odp_timer_test.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_timer_test.c ODP timer example application + */ + +#include <string.h> +#include <stdlib.h> + +/* ODP main header */ +#include <odp.h> + +/* ODP helper for Linux apps */ +#include <helper/odp_linux.h> + +/* GNU lib C */ +#include <getopt.h> + + +#define MAX_WORKERS 32 /**< Max worker threads */ +#define MSG_POOL_SIZE (4*1024*1024) /**< Message pool size */ + +/** Test arguments */ +typedef struct { + int core_count; /**< Core count*/ +} test_args_t; + + +/** @private Barrier for test synchronisation */ +static odp_barrier_t test_barrier; + +/** @private Timer handle*/ +static odp_timer_t test_timer; + + +/** @private test timeout */ +static void test_timeouts(int thr) +{ + uint64_t tick; + odp_queue_t queue; + odp_buffer_t buf; + int num = 10; + + ODP_DBG(" [%i] test_timeouts\n", thr); + + queue = odp_queue_lookup("timer_queue"); + + tick = odp_timer_current_tick(test_timer); + + tick += 100; + + odp_timer_absolute_tmo(test_timer, tick, + queue, ODP_BUFFER_INVALID); + + ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick); + + while (1) { + odp_timeout_t tmo; + + buf = odp_schedule_one(&queue, ODP_SCHED_WAIT); + + tmo = odp_timeout_from_buffer(buf); + tick = odp_timeout_tick(tmo); + + ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick); + + odp_buffer_free(buf); + + num--; + + if (num == 0) + break; + + tick += 100; + + odp_timer_absolute_tmo(test_timer, tick, + queue, ODP_BUFFER_INVALID); + } + + if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC) + odp_schedule_release_atomic(); +} + + +/** + * @internal Worker thread + * + * @param arg Arguments + * + * @return NULL on failure + */ +static void *run_thread(void *arg) +{ + int thr; + odp_buffer_pool_t msg_pool; + + thr = odp_thread_id(); + + printf("Thread %i starts on core %i\n", thr, odp_thread_core()); + + /* + * Test barriers back-to-back + */ + odp_barrier_sync(&test_barrier); + odp_barrier_sync(&test_barrier); + odp_barrier_sync(&test_barrier); + odp_barrier_sync(&test_barrier); + + /* + * Find the buffer pool + */ + msg_pool = odp_buffer_pool_lookup("msg_pool"); + + if (msg_pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR(" [%i] msg_pool not found\n", thr); + return NULL; + } + + odp_barrier_sync(&test_barrier); + + test_timeouts(thr); + + + printf("Thread %i exits\n", thr); + fflush(NULL); + return arg; +} + + +/** + * @internal Print help + */ +static void print_usage(void) +{ + printf("\n\nUsage: ./odp_example [options]\n"); + printf("Options:\n"); + printf(" -c, --count <number> core count, core IDs start from 1\n"); + printf(" -h, --help this help\n"); + printf("\n\n"); +} + + +/** + * @internal Parse arguments + * + * @param argc Argument count + * @param argv Argument vector + * @param args Test arguments + */ +static void parse_args(int argc, char *argv[], test_args_t *args) +{ + int opt; + int long_index; + + static struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + while (1) { + opt = getopt_long(argc, argv, "+c:h", longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'c': + args->core_count = atoi(optarg); + break; + + case 'h': + print_usage(); + exit(EXIT_SUCCESS); + break; + + default: + break; + } + } +} + + +/** + * Test main function + */ +int main(int argc, char *argv[]) +{ + odp_linux_pthread_t thread_tbl[MAX_WORKERS]; + test_args_t args; + int thr_id; + int num_workers; + odp_buffer_pool_t pool; + void *pool_base; + odp_queue_t queue; + int first_core; + uint64_t cycles, ns; + odp_queue_param_t param; + + printf("\nODP example starts\n"); + + memset(&args, 0, sizeof(args)); + parse_args(argc, argv, &args); + + memset(thread_tbl, 0, sizeof(thread_tbl)); + + if (odp_init_global()) { + printf("ODP global init failed.\n"); + return -1; + } + + printf("\n"); + printf("ODP system info\n"); + printf("---------------\n"); + printf("ODP API version: %s\n", odp_version_api_str()); + printf("CPU model: %s\n", odp_sys_cpu_model_str()); + printf("CPU freq (hz): %"PRIu64"\n", odp_sys_cpu_hz()); + printf("Cache line size: %i\n", odp_sys_cache_line_size()); + printf("Max core count: %i\n", odp_sys_core_count()); + + printf("\n"); + + /* A worker thread per core */ + num_workers = odp_sys_core_count(); + + if (args.core_count) + num_workers = args.core_count; + + /* force to max core count */ + if (num_workers > MAX_WORKERS) + num_workers = MAX_WORKERS; + + printf("num worker threads: %i\n", num_workers); + + /* + * By default core #0 runs Linux kernel background tasks. + * Start mapping thread from core #1 + */ + first_core = 1; + + if (odp_sys_core_count() == 1) + first_core = 0; + + printf("first core: %i\n", first_core); + + /* + * Init this thread. It makes also ODP calls when + * setting up resources for worker threads. + */ + thr_id = odp_thread_create(0); + odp_init_local(thr_id); + + /* + * Create message pool + */ + pool_base = odp_shm_reserve("msg_pool", + MSG_POOL_SIZE, ODP_CACHE_LINE_SIZE); + + pool = odp_buffer_pool_create("msg_pool", pool_base, MSG_POOL_SIZE, + 0, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_TIMEOUT); + + if (pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Pool create failed.\n"); + return -1; + } + + /* + * Create a queue for timer test + */ + memset(¶m, 0, sizeof(param)); + param.sched.prio = ODP_SCHED_PRIO_DEFAULT; + param.sched.sync = ODP_SCHED_SYNC_NONE; + param.sched.group = ODP_SCHED_GROUP_DEFAULT; + + queue = odp_queue_create("timer_queue", ODP_QUEUE_TYPE_SCHED, ¶m); + + if (queue == ODP_QUEUE_INVALID) { + ODP_ERR("Timer queue create failed.\n"); + return -1; + } + + test_timer = odp_timer_create("test_timer", pool, + 1000000, 1000000, 1000000000000UL); + + + odp_shm_print_all(); + + printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz()); + printf("Cycles vs nanoseconds:\n"); + ns = 0; + cycles = odp_time_ns_to_cycles(ns); + + printf(" %12"PRIu64" ns -> %12"PRIu64" cycles\n", ns, cycles); + printf(" %12"PRIu64" cycles -> %12"PRIu64" ns\n", cycles, + odp_time_cycles_to_ns(cycles)); + + for (ns = 1; ns <= 100000000000UL; ns *= 10) { + cycles = odp_time_ns_to_cycles(ns); + + printf(" %12"PRIu64" ns -> %12"PRIu64" cycles\n", ns, + cycles); + printf(" %12"PRIu64" cycles -> %12"PRIu64" ns\n", cycles, + odp_time_cycles_to_ns(cycles)); + } + + printf("\n"); + + /* Barrier to sync test case execution */ + odp_barrier_init_count(&test_barrier, num_workers); + + /* Create and launch worker threads */ + odp_linux_pthread_create(thread_tbl, num_workers, first_core, + run_thread, NULL); + + /* Wait for worker threads to exit */ + odp_linux_pthread_join(thread_tbl, num_workers); + + printf("ODP timer test complete\n\n"); + + return 0; +} |