On 7 June 2016 at 11:04, Xuelin Shi <forrest....@linaro.org> wrote: > multi-thread, multi-queue and bi-directional forwarding. > > support (port, queue, core) arguments in cmdline. > it means core will handle rx queue at port. > forwarding logic will decide out port by this specification. > if no this argument in cmdline, default used. > > Signed-off-by: Xuelin Shi <forrest....@linaro.org> > --- > change history: > v3: address the comments of v2 > add argument (port, queue, core) specification support > v2: merge v1 patch set into one patch > > example/Makefile.am | 2 +- > example/l3fwd/.gitignore | 4 + > example/l3fwd/Makefile.am | 12 + > example/l3fwd/jhash.h | 46 +++ > example/l3fwd/odp_l3fwd.c | 744 > +++++++++++++++++++++++++++++++++++++++++++ > example/l3fwd/odp_l3fwd_db.c | 381 ++++++++++++++++++++++ > example/l3fwd/odp_l3fwd_db.h | 122 +++++++ > example/m4/configure.m4 | 1 + > 8 files changed, 1311 insertions(+), 1 deletion(-) > create mode 100644 example/l3fwd/.gitignore > create mode 100644 example/l3fwd/Makefile.am > create mode 100644 example/l3fwd/jhash.h > create mode 100644 example/l3fwd/odp_l3fwd.c > create mode 100644 example/l3fwd/odp_l3fwd_db.c > create mode 100644 example/l3fwd/odp_l3fwd_db.h > > diff --git a/example/Makefile.am b/example/Makefile.am > index 7f82c4d..67e4389 100644 > --- a/example/Makefile.am > +++ b/example/Makefile.am > @@ -1 +1 @@ > -SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt > l2fwd_simple switch > +SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt > l2fwd_simple l3fwd switch > diff --git a/example/l3fwd/.gitignore b/example/l3fwd/.gitignore > new file mode 100644 > index 0000000..4a25ae8 > --- /dev/null > +++ b/example/l3fwd/.gitignore > @@ -0,0 +1,4 @@ > +odp_l3fwd > +Makefile > +Makefile.in > +*.o > diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am > new file mode 100644 > index 0000000..e0dd266 > --- /dev/null > +++ b/example/l3fwd/Makefile.am > @@ -0,0 +1,12 @@ > +include $(top_srcdir)/example/Makefile.inc > + > +bin_PROGRAMS = odp_l3fwd$(EXEEXT) > +odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static > +odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example > + > +noinst_HEADERS = \ > + $(top_srcdir)/example/l3fwd/jhash.h \ > + $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \ > + $(top_srcdir)/example/example_debug.h > + > +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c > diff --git a/example/l3fwd/jhash.h b/example/l3fwd/jhash.h > new file mode 100644 > index 0000000..d3dbd2a > --- /dev/null > +++ b/example/l3fwd/jhash.h > @@ -0,0 +1,46 @@ > +/** jhash.h: Jenkins hash support. > + * > + * Copyright (C) 2006 Bob Jenkins (bob_jenk...@burtleburtle.net) > + * > + * http://burtleburtle.net/bob/hash/ > + * > + * These are the credits from Bob's sources: > + * > + * lookup3.c, by Bob Jenkins, May 2006, Public Domain. > + * > + * These are functions for producing 32-bit hashes for hash table lookup. > + * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() > + * are externally useful functions. Routines to test the hash are included > + * if SELF_TEST is defined. You can use this free for any purpose. It's in > + * the public domain. It has no warranty. > + * > + * $FreeBSD$ > + */ > + > +#ifndef JHASH_H_ > +#define JHASH_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +/** > + * Hash calculation utility > + */ > +#define JHASH_GOLDEN_RATIO 0x9e3779b9 > +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) > +#define FWD_BJ3_MIX(a, b, c) \ > +{ \ > + a -= c; a ^= rot(c, 4); c += b; \ > + b -= a; b ^= rot(a, 6); a += c; \ > + c -= b; c ^= rot(b, 8); b += a; \ > + a -= c; a ^= rot(c, 16); c += b; \ > + b -= a; b ^= rot(a, 19); a += c; \ > + c -= b; c ^= rot(b, 4); b += a; \ > +} > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c > new file mode 100644 > index 0000000..fe715d5 > --- /dev/null > +++ b/example/l3fwd/odp_l3fwd.c > @@ -0,0 +1,744 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <errno.h> > + > +#include <example_debug.h> > +#include <odp_api.h> > +#include <odp/helper/linux.h> > +#include <odp/helper/eth.h> > +#include <odp/helper/ip.h> > +#include <odp/helper/udp.h> > +#include <odp/helper/tcp.h> > + > +#include <getopt.h> > + > +#include "odp_l3fwd_db.h" > + > +#define POOL_NUM_PKT 8192 > +#define POOL_SEG_LEN 1856 > +#define MAX_PKT_BURST 32 > + > +#define MAX_NB_WORKER 8 > +#define MAX_NB_PKTIO 4 > +#define MAX_NB_ROUTE 32 > +#define MAX_NB_RX_QUEUE 8 > +#define MAX_NB_TX_QUEUE 8 > +#define MAX_NB_QCONFS 1024 > +#define MAX_NB_QCONF_PER_CORE 8 > +#define CPU_ID_INVALID (-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)) > + > +struct l3fwd_pktio_s { > + odp_pktio_t pktio; > + odp_pktin_queue_t ifin[MAX_NB_RX_QUEUE]; > + odp_pktout_queue_t ifout[MAX_NB_TX_QUEUE]; > + uint32_t nb_rxq; > + uint32_t nb_txq; > +}; > + > +struct l3fwd_qconf_s { > + uint8_t if_idx; /* port index */ > + uint8_t rxq_idx; /* recv queue index in a port */ > + uint8_t core_idx; /* this core should handle traffic */ > +}; > + > +struct thread_arg_s { > + int cpu; /* initialized as -1 */ > + struct l3fwd_qconf_s *qconf_args[MAX_NB_QCONF_PER_CORE]; > + int count; > +}; > + > +static struct l3fwd_qconf_s qconf_config_default[] = { > + {0, 0, 1}, > + {1, 0, 2}, > + {2, 0, 3}, > + {3, 0, 4}, > + {0, 1, 5}, > + {1, 1, 6}, > + {2, 1, 7}, > + {3, 1, 0}, > +}; > + > +typedef struct { > + char *if_names[MAX_NB_PKTIO]; > + int if_count; > + char *route_str[MAX_NB_ROUTE]; > + int worker_count; > + struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS]; > + int qconf_count; > +} app_args_t; > + > +struct { > + app_args_t cmd_args; > + struct l3fwd_pktio_s l3fwd_pktios[MAX_NB_PKTIO]; > + odph_linux_pthread_t l3fwd_workers[MAX_NB_WORKER];
please use odph_odpthread_t and the set of odph_odpthread* function instead > + struct thread_arg_s worker_args[MAX_NB_WORKER]; > + uint32_t nb_pktio; /* effective pktios */ > + uint32_t nb_worker; /* effective workers */ > +} global; > + > +static void print_usage(char *progname); > +static void print_info(char *progname, app_args_t *args); > +static void parse_cmdline_args(int argc, char *argv[], app_args_t *args); > +static int parse_config(char *cfg_str, app_args_t *args); > +static inline void setup_worker_qconf(app_args_t *args); > +static int split_string(char *str, int stringlen, > + char **tokens, int maxtokens, char delim); > + > +static odp_pktio_t create_pktio(const char *name, odp_pool_t pool, > + struct l3fwd_pktio_s *fwd_pktio) > +{ > + odp_pktio_param_t pktio_param; > + odp_pktin_queue_param_t in_queue_param; > + odp_pktout_queue_param_t out_queue_param; > + odp_pktio_t pktio; > + odp_pktio_capability_t capa; > + struct odp_pktin_queue_t *inq; > + struct odp_pktout_queue_t *outq; > + int rc, nb_rxq, nb_txq; > + > + odp_pktio_param_init(&pktio_param); > + > + pktio = odp_pktio_open(name, pool, &pktio_param); > + if (pktio == ODP_PKTIO_INVALID) { > + printf("Failed to open %s\n", name); > + exit(1); > + } > + > + odp_pktin_queue_param_init(&in_queue_param); > + odp_pktout_queue_param_init(&out_queue_param); > + > + /** set rx and tx queue number > + * 0 < rxq <= capability: set to rxq, else to rx capa > + * 0 < txq <= capability: set to txq, else to tx capa > + */ > + rc = odp_pktio_capability(pktio, &capa); > + if (rc) { > + printf("pktio %s: unable to read capabilities, " > + "set to 1 rx and 1 tx queue\n", name); > + fwd_pktio->nb_rxq = 1; > + fwd_pktio->nb_txq = 1; > + } > + > + nb_rxq = fwd_pktio->nb_rxq; > + nb_txq = fwd_pktio->nb_txq; > + if (nb_rxq == 0) { > + fwd_pktio->nb_rxq = 1; > + } else if (nb_rxq > (int)capa.max_input_queues) { > + printf("Error queue config: max queue %d for %s.\n", > + (int)capa.max_input_queues - 1, name); > + exit(1); > + } > + > + if (nb_txq == 0) > + fwd_pktio->nb_txq = 1; > + else if (nb_txq > (int)capa.max_output_queues) > + fwd_pktio->nb_txq = capa.max_output_queues; > + > + in_queue_param.num_queues = fwd_pktio->nb_rxq; > + out_queue_param.num_queues = fwd_pktio->nb_txq; > + > + in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE; > + if (odp_pktin_queue_config(pktio, &in_queue_param)) { > + printf("Failed to config input queue for %s\n", name); > + exit(1); > + } > + > + out_queue_param.op_mode = ODP_PKTIO_OP_MT; > + if (odp_pktout_queue_config(pktio, &out_queue_param)) { > + printf("Failed to config output queue for %s\n", name); > + exit(1); > + } > + > + nb_rxq = in_queue_param.num_queues; > + nb_txq = out_queue_param.num_queues; > + inq = &fwd_pktio->ifin[0]; > + if (odp_pktin_queue(pktio, inq, nb_rxq) != nb_rxq) { > + printf("pktin queue query failed for %s\n", name); > + exit(1); > + } > + > + outq = &fwd_pktio->ifout[0]; > + if (odp_pktout_queue(pktio, outq, nb_txq) != nb_txq) { > + printf("pktout queue query failed for %s\n", name); > + exit(1); > + } > + > + fwd_pktio->pktio = pktio; > + > + return pktio; > +} > + > +static inline void l3fwd_one_queue(odp_pktin_queue_t inq, > + odp_pktout_queue_t def_outq) > +{ > + odp_packet_t pkt_tbl[MAX_PKT_BURST]; > + odp_packet_t pkt_tbl_drop[MAX_PKT_BURST]; > + uint32_t pkts, i; > + int need_to_drop = 0; > + int ret; > + > + pkts = odp_pktin_recv(inq, pkt_tbl, MAX_PKT_BURST); > + if (odp_unlikely(pkts <= 0)) > + return; > + > + for (i = 0; i < pkts; i++) { > + odp_packet_t pkt = pkt_tbl[i]; > + odph_ethhdr_t *eth; > + odph_ipv4hdr_t *ip; > + odph_udphdr_t *udp; > + fwd_db_entry_t *entry; > + ipv4_tuple5_t key; > + uint32_t len; > + > + if (odp_unlikely(!odp_packet_has_ipv4(pkt) || > + !odp_packet_has_eth(pkt))) { > + printf("warning: invalid ipv4 or eth header\n"); > + pkt_tbl_drop[need_to_drop] = pkt; > + need_to_drop++; > + continue; > + } > + > + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, &len); > + key.dst_ip = odp_be_to_cpu_32(ip->dst_addr); > + key.src_ip = odp_be_to_cpu_32(ip->src_addr); > + key.proto = ip->proto; > + if (odp_packet_has_udp(pkt) || > + odp_packet_has_tcp(pkt)) { > + /* UDP or TCP*/ > + void *ptr = odp_packet_l4_ptr(pkt, NULL); > + > + udp = (odph_udphdr_t *)ptr; > + key.src_port = odp_be_to_cpu_16(udp->src_port); > + key.dst_port = odp_be_to_cpu_16(udp->dst_port); > + } else { > + key.src_port = 0; > + key.dst_port = 0; > + } > + > + entry = find_fwd_db_entry(&key); > + ip->ttl--; > + ip->chksum = odph_ipv4_csum_update(pkt); > + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); > + if (entry) { > + memcpy(eth->src.addr, entry->src_mac, > ODPH_ETHADDR_LEN); > + memcpy(eth->dst.addr, entry->dst_mac, > ODPH_ETHADDR_LEN); > + ret = odp_pktout_send(entry->outq, &pkt, 1); > + } else { > + /* on route, reflect the packet */ > + memcpy(eth->dst.addr, eth->src.addr, > ODPH_ETHADDR_LEN); > + ret = odp_pktout_send(def_outq, &pkt, 1); > + } > + if (ret < 1) { > + pkt_tbl_drop[need_to_drop] = pkt; > + need_to_drop++; > + } > + } > + > + odp_packet_free_multi(pkt_tbl_drop, need_to_drop); > +} > + > +static void *run_worker(void *arg) > +{ > + struct thread_arg_s *args = arg; > + > + for (;;) { > + int i; > + struct l3fwd_pktio_s *port; > + struct l3fwd_qconf_s *qconf; > + > + for (i = 0; i < args->count; i++) { > + qconf = args->qconf_args[i]; > + if (args->cpu != qconf->core_idx) > + continue; > + > + port = &global.l3fwd_pktios[qconf->if_idx]; > + l3fwd_one_queue(port->ifin[qconf->rxq_idx], > + port->ifout[0]); > + } > + } > + > + return NULL; > +} > + > +int main(int argc, char **argv) > +{ > + odp_pool_t pool; > + odp_pool_param_t params; > + odp_instance_t instance; > + odph_linux_thr_params_t thr_params; > + int cpu; > + uint32_t i, nb_worker; > + uint8_t mac[ODPH_ETHADDR_LEN]; > + app_args_t *args; > + > + if (odp_init_global(&instance, NULL, NULL)) { > + printf("Error: ODP global init failed.\n"); > + exit(1); > + } > + > + if (odp_init_local(instance, ODP_THREAD_CONTROL)) { > + printf("Error: ODP local init failed.\n"); > + exit(1); > + } > + > + /* Clear global argument */ > + memset(&global, 0, sizeof(global)); > + > + /* Parse cmdline arguments */ > + args = &global.cmd_args; > + parse_cmdline_args(argc, argv, args); > + > + /* Distribute the receive queues by core */ > + setup_worker_qconf(args); > + > + /* Init l3fwd tale */ > + init_fwd_db(); > + > + /* Add route into table */ > + for (i = 0; i < MAX_NB_ROUTE; i++) { > + if (args->route_str[i]) > + create_fwd_db_entry(args->route_str[i]); > + } > + > + print_info(NO_PATH(argv[0]), args); > + > + /* Create packet pool */ > + odp_pool_param_init(¶ms); > + params.pkt.seg_len = POOL_SEG_LEN; > + params.pkt.len = POOL_SEG_LEN; > + params.pkt.num = POOL_NUM_PKT; > + params.type = ODP_POOL_PACKET; > + > + pool = odp_pool_create("packet pool", ¶ms); > + > + if (pool == ODP_POOL_INVALID) { > + printf("Error: packet pool create failed.\n"); > + exit(1); > + } > + > + global.nb_pktio = args->if_count; > + for (i = 0; i < global.nb_pktio; i++) { > + struct l3fwd_pktio_s *port; > + char *if_name; > + char buf[16]; > + int j; > + > + if_name = args->if_names[i]; > + port = &global.l3fwd_pktios[i]; > + port->pktio = create_pktio(if_name, pool, port); > + odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN); > + resolve_fwd_db(if_name, port->ifout[0], mac); > + > + /* print mac string, could be used to config pktgen */ > + sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", > + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); > + printf("create pktio %s, mac %s\n", if_name, buf); > + > + /* start pktio */ > + if (odp_pktio_start(port->pktio)) { > + printf("unable to start pktio: %s\n", if_name); > + exit(1); > + } > + > + printf("start pktio: %s, rxq: ", if_name); > + for (j = 0; j < args->qconf_count; j++) { > + struct l3fwd_qconf_s *q; > + > + q = &args->qconf_config[j]; > + if (q->if_idx == i) > + printf("(%d, %d, %d) ", q->if_idx, q->rxq_idx, > + q->core_idx); > + } > + printf("\n"); > + } > + > + dump_fwd_db(); > + > + /* one worker thread per core. > + * if no thread count in cmdline, all cpus are used, > + * else create threads as the cmdline. > + */ > + cpu = odp_cpu_count(); > + if (args->worker_count == 0 || args->worker_count > cpu) > + args->worker_count = cpu; > + > + global.nb_worker = args->worker_count; > + memset(&thr_params, 0, sizeof(thr_params)); > + thr_params.start = run_worker; > + thr_params.thr_type = ODP_THREAD_WORKER; > + thr_params.instance = instance; > + > + nb_worker = 0; > + cpu = odp_cpu_id(); > + for (i = 0; i < MAX_NB_WORKER; i++) { > + struct thread_arg_s *arg; > + odp_cpumask_t thr_mask; > + > + /* not create thread for current and invalid cpu */ > + arg = &global.worker_args[i]; > + if (arg->cpu == cpu || arg->cpu == CPU_ID_INVALID) > + continue; > + > + odp_cpumask_zero(&thr_mask); > + odp_cpumask_set(&thr_mask, arg->cpu); > + thr_params.arg = arg; > + odph_linux_pthread_create(&global.l3fwd_workers[i], please use odph_odpthreads_create() &thr_mask, > + &thr_params); > + nb_worker++; > + if (nb_worker == global.nb_worker) > + break; > + } > + > + /* if other cores could run the tasks, not run on this cpu */ > + if (nb_worker < global.nb_worker) > + run_worker(&global.l3fwd_workers[cpu]); > + > + /* wait for other threads to join */ > + for (i = 0; i < MAX_NB_WORKER; i++) { > + odph_linux_pthread_t *arg; > + > + arg = &global.l3fwd_workers[i]; > + if (arg->cpu == cpu || arg->cpu == CPU_ID_INVALID) > + continue; > + > + odph_linux_pthread_join(arg, 1); please use odph_odpthreads_join() Christophe. > + } > + > + return 0; > +} > + > +static void print_usage(char *progname) > +{ > + printf("\n" > + "ODP L3 forwarding application.\n" > + "\n" > + "Usage: %s OPTIONS\n" > + " E.g. %s -i eth0,eth1 -r 1.1.1.0/24:eth0 -r > 2.2.2.0/24:eth1\n" > + " In the above example,\n" > + " eth0 will send pkts to eth1 and vice versa\n" > + "\n" > + "Mandatory OPTIONS:\n" > + " -i, --interface eth interfaces (comma-separated, no > spaces)\n" > + " -r, --route SubNet:Intf[:NextHopMAC]\n" > + " NextHopMAC can be optional, in this case, zeroed > mac\n" > + "\n" > + "Optional OPTIONS:\n" > + " -t, --thread Number of threads to do forwarding\n" > + " optional, default as cpu count\n" > + " -q, --queue Configure rx queue(s) for port\n" > + " optional, format: [(port, queue, core),...]\n" > + " for example: -q '(0, 0, 1),(1,0,2)'\n" > + " -h, --help Display help and exit.\n\n" > + "\n", NO_PATH(progname), NO_PATH(progname) > + ); > +} > + > +static void parse_cmdline_args(int argc, char *argv[], app_args_t *args) > +{ > + int opt; > + int long_index; > + char *token, *local; > + size_t len, route_index = 0; > + int i, mem_failure = 0; > + > + static struct option longopts[] = { > + {"interface", required_argument, NULL, 'i'}, /* return 'i' > */ > + {"route", required_argument, NULL, 'r'}, /* return 'r' > */ > + {"thread", optional_argument, NULL, 't'}, /* return 't' > */ > + {"queue", optional_argument, NULL, 'q'}, /* return 'q' > */ > + {"help", no_argument, NULL, 'h'}, /* return 'h' > */ > + {NULL, 0, NULL, 0} > + }; > + > + while (1) { > + opt = getopt_long(argc, argv, "+t:i:r:q:h", > + longopts, &long_index); > + > + if (opt == -1) > + break; /* No more options */ > + > + switch (opt) { > + /* parse number of worker threads to be run*/ > + case 't': > + i = odp_cpu_count(); > + args->worker_count = atoi(optarg); > + if (args->worker_count > i) { > + printf("Too many threads," > + "truncate to cpu count: %d\n", i); > + args->worker_count = i; > + } > + > + break; > + /* parse packet-io interface names */ > + case 'i': > + len = strlen(optarg); > + if (len == 0) { > + print_usage(argv[0]); > + exit(EXIT_FAILURE); > + } > + len += 1; /* add room for '\0' */ > + > + local = malloc(len); > + if (!local) { > + print_usage(argv[0]); > + exit(EXIT_FAILURE); > + } > + > + /* count the number of tokens separated by ',' */ > + strcpy(local, optarg); > + for (token = strtok(local, ","), i = 0; > + token != NULL; > + token = strtok(NULL, ","), i++) > + ; > + > + if (i == 0) { > + print_usage(argv[0]); > + exit(EXIT_FAILURE); > + } else if (i > MAX_NB_PKTIO) { > + printf("too many ports specified, " > + "truncated to %d", MAX_NB_PKTIO); > + } > + args->if_count = i; > + > + /* store the if names (reset names string) */ > + strcpy(local, optarg); > + for (token = strtok(local, ","), i = 0; > + token != NULL; token = strtok(NULL, ","), i++) { > + args->if_names[i] = token; > + } > + break; > + > + /*Configure Route in forwarding database*/ > + case 'r': > + if (route_index >= MAX_NB_ROUTE) { > + printf("No more routes can be added\n"); > + break; > + } > + local = calloc(1, strlen(optarg) + 1); > + if (!local) { > + mem_failure = 1; > + break; > + } > + memcpy(local, optarg, strlen(optarg)); > + local[strlen(optarg)] = '\0'; > + args->route_str[route_index++] = local; > + break; > + > + case 'h': > + print_usage(argv[0]); > + exit(EXIT_SUCCESS); > + break; > + > + case 'q': > + parse_config(optarg, args); > + break; > + > + default: > + break; > + } > + } > + > + /* checking arguments */ > + if (args->if_count == 0) { > + printf("\nNo option -i specified.\n"); > + goto out; > + } > + > + if (args->route_str[0] == NULL) { > + printf("\nNo option -r specified.\n"); > + goto out; > + } > + > + if (mem_failure == 1) { > + printf("\nAllocate memory failure.\n"); > + goto out; > + } > + optind = 1; /* reset 'extern optind' from the getopt lib > */ > + return; > + > +out: > + print_usage(argv[0]); > + exit(EXIT_FAILURE); > +} > + > +/* split string into tokens */ > +int split_string(char *str, int stringlen, > + char **tokens, int maxtokens, char delim) > +{ > + int i, tok = 0; > + int tokstart = 1; /* first token is right at start of string */ > + > + if (str == NULL || tokens == NULL) > + goto einval_error; > + > + for (i = 0; i < stringlen; i++) { > + if (str[i] == '\0' || tok >= maxtokens) > + break; > + if (tokstart) { > + tokstart = 0; > + tokens[tok++] = &str[i]; > + } > + if (str[i] == delim) { > + str[i] = '\0'; > + tokstart = 1; > + } > + } > + return tok; > + > +einval_error: > + errno = EINVAL; > + return -1; > +} > + > +static int parse_config(char *cfg_str, app_args_t *args) > +{ > + char s[256]; > + const char *p, *p0 = cfg_str; > + char *end; > + enum fieldnames { > + FLD_PORT = 0, > + FLD_QUEUE, > + FLD_LCORE, > + FLD_LAST > + }; > + unsigned long int_fld[FLD_LAST]; > + char *str_fld[FLD_LAST]; > + int i; > + unsigned size; > + int nb_qconfs = 0; > + struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0]; > + > + p = strchr(p0, '('); > + while (p != NULL) { > + ++p; > + p0 = strchr(p, ')'); > + if (p0 == NULL) > + return -1; > + > + size = p0 - p; > + if (size >= sizeof(s)) > + return -1; > + > + snprintf(s, sizeof(s), "%.*s", size, p); > + i = split_string(s, sizeof(s), str_fld, FLD_LAST, ','); > + if (i != FLD_LAST) > + return -1; > + for (i = 0; i < FLD_LAST; i++) { > + errno = 0; > + int_fld[i] = strtoul(str_fld[i], &end, 0); > + if (errno != 0 || end == str_fld[i] || int_fld[i] > > 255) > + return -1; > + } > + if (nb_qconfs >= MAX_NB_QCONFS) { > + printf("exceeded max number of queue params: %hu\n", > + nb_qconfs); > + return -1; > + } > + qconf_array[nb_qconfs].if_idx = (uint8_t)int_fld[FLD_PORT]; > + qconf_array[nb_qconfs].rxq_idx = (uint8_t)int_fld[FLD_QUEUE]; > + qconf_array[nb_qconfs].core_idx = (uint8_t)int_fld[FLD_LCORE]; > + ++nb_qconfs; > + } > + > + args->qconf_count = nb_qconfs; > + > + return 0; > +} > + > +static void print_info(char *progname, app_args_t *args) > +{ > + int i; > + > + printf("\n" > + "ODP system info\n" > + "---------------\n" > + "ODP API version: %s\n" > + "ODP impl name: %s\n" > + "CPU model: %s\n" > + "CPU freq (hz): %" PRIu64 "\n" > + "Cache line size: %i\n" > + "CPU count: %i\n" > + "\n", > + odp_version_api_str(), odp_version_impl_name(), > + odp_cpu_model_str(), odp_cpu_hz_max(), > + odp_sys_cache_line_size(), odp_cpu_count()); > + > + printf("Running ODP appl: \"%s\"\n" > + "-----------------\n" > + "IF-count: %i\n" > + "Using IFs: ", > + progname, args->if_count); > + > + for (i = 0; i < args->if_count; ++i) > + printf(" %s", args->if_names[i]); > + > + printf("\n\n"); > + fflush(NULL); > +} > + > +static void setup_worker_qconf(app_args_t *args) > +{ > + int i; > + int cpu_count; > + struct l3fwd_qconf_s *q; > + struct thread_arg_s *arg; > + struct l3fwd_pktio_s *port; > + uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_RX_QUEUE]; > + > + cpu_count = odp_cpu_count(); > + > + /* initialise the worker cpu as -1 */ > + for (i = 0; i < MAX_NB_WORKER; i++) { > + arg = &global.worker_args[i]; > + arg->cpu = CPU_ID_INVALID; > + } > + memset(&queue_mask[0][0], 0, sizeof(queue_mask)); > + > + /* if no queue config specified, use default */ > + if (args->qconf_count == 0) { > + int len, tmp; > + > + tmp = cpu_count < args->if_count ? cpu_count : args->if_count; > + len = tmp * sizeof(qconf_config_default[0]); > + memcpy(&args->qconf_config[0], &qconf_config_default[0], len); > + args->qconf_count = tmp; > + } > + > + for (i = 0; i < args->qconf_count; i++) { > + q = &args->qconf_config[i]; > + if (q->core_idx >= cpu_count || q->if_idx >= args->if_count) { > + printf("Error queue config: max port: %d, " > + "max core: %d\n", args->if_count - 1, > + cpu_count - 1); > + exit(1); > + } > + > + /* check if one queue is configured twice or more */ > + if (queue_mask[q->if_idx][q->rxq_idx]) { > + printf("Error queue config: re-config port: %d, " > + "queue: %d\n", q->if_idx, q->rxq_idx); > + exit(1); > + } > + queue_mask[q->if_idx][q->rxq_idx] = 1; > + > + port = &global.l3fwd_pktios[q->if_idx]; > + if (port->nb_rxq <= q->rxq_idx) > + port->nb_rxq = q->rxq_idx + 1; > + > + /* put the queue into worker_args */ > + arg = &global.worker_args[q->core_idx]; > + arg->qconf_args[arg->count] = q; > + arg->count++; > + arg->cpu = q->core_idx; > + } > +} > diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c > new file mode 100644 > index 0000000..4767a9b > --- /dev/null > +++ b/example/l3fwd/odp_l3fwd_db.c > @@ -0,0 +1,381 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +/* enable strtok */ > +#ifndef _GNU_SOURCE > +#define _GNU_SOURCE > +#endif > + > +#include <stdlib.h> > +#include <string.h> > + > +#include <example_debug.h> > + > +#include <odp_api.h> > + > +#include <odp_l3fwd_db.h> > +#include <jhash.h> > + > +/** > + * Compute hash value from a flow > + */ > +static inline > +uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key) > +{ > + uint64_t l4_ports = 0; > + uint32_t dst_ip, src_ip; > + > + src_ip = key->src_ip; > + dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO; > + FWD_BJ3_MIX(src_ip, dst_ip, l4_ports); > + > + return l4_ports; > +} > + > +/** > + * Parse text string representing an IPv4 address or subnet > + * > + * String is of the format "XXX.XXX.XXX.XXX(/W)" where > + * "XXX" is decimal value and "/W" is optional subnet length > + * > + * @param ipaddress Pointer to IP address/subnet string to convert > + * @param addr Pointer to return IPv4 address > + * @param mask Pointer (optional) to return IPv4 mask > + * > + * @return 0 if successful else -1 > + */ > +static inline > +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *mask) > +{ > + int b[4]; > + int qualifier = 32; > + int converted; > + > + if (strchr(ipaddress, '/')) { > + converted = sscanf(ipaddress, "%d.%d.%d.%d/%d", > + &b[3], &b[2], &b[1], &b[0], > + &qualifier); > + if (5 != converted) > + return -1; > + } else { > + converted = sscanf(ipaddress, "%d.%d.%d.%d", > + &b[3], &b[2], &b[1], &b[0]); > + if (4 != converted) > + return -1; > + } > + > + if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255)) > + return -1; > + if (!qualifier || (qualifier > 32)) > + return -1; > + > + *addr = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24; > + if (mask) > + *mask = ~(0xFFFFFFFF & ((1ULL << (32 - qualifier)) - 1)); > + > + return 0; > +} > + > +/** > + * Generate text string representing IPv4 range/subnet, output > + * in "XXX.XXX.XXX.XXX/W" format > + * > + * @param b Pointer to buffer to store string > + * @param range Pointer to IPv4 address range > + * > + * @return Pointer to supplied buffer > + */ > +static inline > +char *ipv4_subnet_str(char *b, ip_addr_range_t *range) > +{ > + int idx; > + int len; > + > + for (idx = 0; idx < 32; idx++) > + if (range->mask & (1 << idx)) > + break; > + len = 32 - idx; > + > + sprintf(b, "%03d.%03d.%03d.%03d/%d", > + 0xFF & ((range->addr) >> 24), > + 0xFF & ((range->addr) >> 16), > + 0xFF & ((range->addr) >> 8), > + 0xFF & ((range->addr) >> 0), > + len); > + return b; > +} > + > +/** > + * Generate text string representing MAC address > + * > + * @param b Pointer to buffer to store string > + * @param mac Pointer to MAC address > + * > + * @return Pointer to supplied buffer > + */ > +static inline > +char *mac_addr_str(char *b, uint8_t *mac) > +{ > + sprintf(b, "%02X.%02X.%02X.%02X.%02X.%02X", > + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); > + return b; > +} > + > +/** > + * Flow cache table entry > + */ > +typedef struct flow_entry_s { > + ipv4_tuple5_t key; /**< match key */ > + struct flow_entry_s *next; /**< next entry on the list */ > + fwd_db_entry_t *fwd_entry; /**< entry info in db */ > +} flow_entry_t; > + > +/** > + * Flow cache table bucket > + */ > +typedef struct flow_bucket_s { > + odp_spinlock_t lock; /**< Bucket lock*/ > + flow_entry_t *next; /**< Pointer to first flow entry in > bucket*/ > +} flow_bucket_t; > + > +/** > + * Flow hash table, fast lookup cache > + */ > +typedef struct flow_table_s { > + flow_bucket_t *bucket; > + uint32_t count; > +} flow_table_t; > + > +static flow_table_t fwd_lookup_cache; > + > +static inline > +void init_fwd_cache(void) > +{ > + odp_shm_t hash_shm; > + flow_bucket_t *bucket; > + uint32_t bucket_count; > + uint32_t i; > + > + bucket_count = FWD_DEF_BUCKET_COUNT; > + > + /*Reserve memory for Routing hash table*/ > + hash_shm = odp_shm_reserve("route_table", > + sizeof(flow_bucket_t) * bucket_count, > + ODP_CACHE_LINE_SIZE, 0); > + > + bucket = odp_shm_addr(hash_shm); > + if (!bucket) { > + EXAMPLE_ERR("Error: shared mem alloc failed.\n"); > + exit(-1); > + } > + > + fwd_lookup_cache.bucket = bucket; > + fwd_lookup_cache.count = bucket_count; > + > + /*Initialize Locks*/ > + for (i = 0; i < bucket_count; i++) { > + bucket = &fwd_lookup_cache.bucket[i]; > + odp_spinlock_init(&bucket->lock); > + bucket->next = NULL; > + } > +} > + > +static inline > +int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow) > +{ > + if (key->src_ip == flow->key.src_ip && > + key->dst_ip == flow->key.dst_ip && > + key->src_port == flow->key.src_port && > + key->dst_port == flow->key.dst_port && > + key->proto == flow->key.proto) > + return 1; > + > + return 0; > +} > + > +static inline > +flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t *bucket) > +{ > + flow_entry_t *rst; > + > + for (rst = bucket->next; rst != NULL; rst = rst->next) { > + if (match_key_flow(key, rst)) > + break; > + } > + > + return rst; > +} > + > +static inline > +flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key, > + flow_bucket_t *bucket, > + fwd_db_entry_t *entry) > +{ > + flow_entry_t *flow; > + > + flow = lookup_fwd_cache(key, bucket); > + if (flow) > + return flow; > + > + flow = malloc(sizeof(flow_entry_t)); > + flow->key = *key; > + flow->fwd_entry = entry; > + > + odp_spinlock_lock(&bucket->lock); > + if (!bucket->next) { > + bucket->next = flow; > + } else { > + flow->next = bucket->next; > + bucket->next = flow; > + } > + odp_spinlock_unlock(&bucket->lock); > + > + return flow; > +} > + > +/** Global pointer to fwd db */ > +fwd_db_t *fwd_db; > + > +void init_fwd_db(void) > +{ > + odp_shm_t shm; > + > + shm = odp_shm_reserve("shm_fwd_db", > + sizeof(fwd_db_t), > + ODP_CACHE_LINE_SIZE, > + 0); > + > + fwd_db = odp_shm_addr(shm); > + > + if (fwd_db == NULL) { > + EXAMPLE_ERR("Error: shared mem alloc failed.\n"); > + exit(EXIT_FAILURE); > + } > + memset(fwd_db, 0, sizeof(*fwd_db)); > + > + init_fwd_cache(); > +} > + > +int create_fwd_db_entry(char *input) > +{ > + int pos = 0; > + char *local; > + char *str; > + char *save; > + char *token; > + fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index]; > + > + /* Verify we haven't run out of space */ > + if (MAX_DB <= fwd_db->index) > + return -1; > + > + /* Make a local copy */ > + local = malloc(strlen(input) + 1); > + if (NULL == local) > + return -1; > + strcpy(local, input); > + > + /* Setup for using "strtok_r" to search input string */ > + str = local; > + save = NULL; > + > + /* Parse tokens separated by ':' */ > + while (NULL != (token = strtok_r(str, ":", &save))) { > + str = NULL; /* reset str for subsequent strtok_r calls */ > + > + /* Parse token based on its position */ > + switch (pos) { > + case 0: > + parse_ipv4_string(token, > + &entry->subnet.addr, > + &entry->subnet.mask); > + break; > + case 1: > + strncpy(entry->oif, token, OIF_LEN - 1); > + entry->oif[OIF_LEN - 1] = 0; > + break; > + case 2: > + odph_eth_addr_parse((odph_ethaddr_t *)entry->dst_mac, > + token); > + break; > + > + default: > + printf("ERROR: extra token \"%s\" at position %d\n", > + token, pos); > + break; > + } > + > + /* Advance to next position */ > + pos++; > + } > + > + /* Add route to the list */ > + fwd_db->index++; > + entry->next = fwd_db->list; > + fwd_db->list = entry; > + > + free(local); > + return 0; > +} > + > +void resolve_fwd_db(char *intf, odp_pktout_queue_t outq, uint8_t *mac) > +{ > + fwd_db_entry_t *entry; > + > + /* Walk the list and attempt to set output and MAC */ > + for (entry = fwd_db->list; NULL != entry; entry = entry->next) { > + if (strcmp(intf, entry->oif)) > + continue; > + > + entry->outq = outq; > + memcpy(entry->src_mac, mac, ODPH_ETHADDR_LEN); > + } > +} > + > +void dump_fwd_db_entry(fwd_db_entry_t *entry) > +{ > + char subnet_str[MAX_STRING]; > + char mac_str[MAX_STRING]; > + > + printf(" %s %s %s\n", > + ipv4_subnet_str(subnet_str, &entry->subnet), > + entry->oif, > + mac_addr_str(mac_str, entry->dst_mac)); > +} > + > +void dump_fwd_db(void) > +{ > + fwd_db_entry_t *entry; > + > + printf("\n" > + "Routing table\n" > + "-------------\n"); > + > + for (entry = fwd_db->list; NULL != entry; entry = entry->next) > + dump_fwd_db_entry(entry); > +} > + > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key) > +{ > + fwd_db_entry_t *entry; > + flow_entry_t *flow; > + flow_bucket_t *bucket; > + uint64_t hash; > + > + /* first find in cache */ > + hash = l3fwd_calc_hash(key); > + hash &= fwd_lookup_cache.count - 1; > + bucket = &fwd_lookup_cache.bucket[hash]; > + flow = lookup_fwd_cache(key, bucket); > + if (flow) > + return flow->fwd_entry; > + > + for (entry = fwd_db->list; NULL != entry; entry = entry->next) > + if (entry->subnet.addr == (key->dst_ip & entry->subnet.mask)) > + break; > + > + return entry; > +} > diff --git a/example/l3fwd/odp_l3fwd_db.h b/example/l3fwd/odp_l3fwd_db.h > new file mode 100644 > index 0000000..eac142e > --- /dev/null > +++ b/example/l3fwd/odp_l3fwd_db.h > @@ -0,0 +1,122 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#ifndef ODP_L3FWD_DB_H_ > +#define ODP_L3FWD_DB_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#include <odp_api.h> > +#include <odp/helper/eth.h> > + > +#define OIF_LEN 32 > +#define MAX_DB 32 > +#define MAX_STRING 32 > + > +/** > + * Default number of flows > + */ > +#define FWD_DEF_FLOW_COUNT 100000 > + > +/** > + * Default Hash bucket number > + */ > +#define FWD_DEF_BUCKET_COUNT (FWD_DEF_FLOW_COUNT / 8) > + > +/** > + * IP address range (subnet) > + */ > +typedef struct ip_addr_range_s { > + uint32_t addr; /**< IP address */ > + uint32_t mask; /**< mask, 1 indicates bits are valid */ > +} ip_addr_range_t; > + > +/** > + * TCP/UDP flow > + */ > +typedef struct ipv4_tuple5_s { > + uint32_t src_ip; > + uint32_t dst_ip; > + uint16_t src_port; > + uint16_t dst_port; > + uint8_t proto; > +} ipv4_tuple5_t; > + > +/** > + * Forwarding data base entry > + */ > +typedef struct fwd_db_entry_s { > + struct fwd_db_entry_s *next; /**< Next entry on list */ > + char oif[OIF_LEN]; /**< Output interface name */ > + odp_pktout_queue_t outq; /**< Output transmit queue */ > + uint8_t src_mac[ODPH_ETHADDR_LEN]; /**< Output source MAC */ > + uint8_t dst_mac[ODPH_ETHADDR_LEN]; /**< Output destination MAC */ > + ip_addr_range_t subnet; /**< Subnet for this router */ > +} fwd_db_entry_t; > + > +/** > + * Forwarding data base > + */ > +typedef struct fwd_db_s { > + uint32_t index; /**< Next available entry */ > + fwd_db_entry_t *list; /**< List of active routes */ > + fwd_db_entry_t array[MAX_DB]; /**< Entry storage */ > +} fwd_db_t; > + > +/** Global pointer to fwd db */ > +extern fwd_db_t *fwd_db; > + > +/** Initialize FWD DB */ > +void init_fwd_db(void); > + > +/** > + * Create a forwarding database entry > + * > + * String is of the format "SubNet:Intf:NextHopMAC" > + * > + * @param input Pointer to string describing route > + * > + * @return 0 if successful else -1 > + */ > +int create_fwd_db_entry(char *input); > + > +/** > + * Scan FWD DB entries and resolve output queue and source MAC address > + * > + * @param intf Interface name string > + * @param outq Output queue for packet transmit > + * @param mac MAC address of this interface > + */ > +void resolve_fwd_db(char *intf, odp_pktout_queue_t outq, uint8_t *mac); > + > +/** > + * Display one forwarding database entry > + * > + * @param entry Pointer to entry to display > + */ > +void dump_fwd_db_entry(fwd_db_entry_t *entry); > + > +/** > + * Display the forwarding database > + */ > +void dump_fwd_db(void); > + > +/** > + * Find a matching forwarding database entry > + * > + * @param key ipv4 tuple > + * > + * @return pointer to forwarding DB entry else NULL > + */ > +fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/example/m4/configure.m4 b/example/m4/configure.m4 > index 9731d81..7868574 100644 > --- a/example/m4/configure.m4 > +++ b/example/m4/configure.m4 > @@ -19,4 +19,5 @@ AC_CONFIG_FILES([example/classifier/Makefile > example/timer/Makefile > example/traffic_mgmt/Makefile > example/l2fwd_simple/Makefile > + example/l3fwd/Makefile > example/switch/Makefile]) > -- > 2.1.0.27.g96db324 > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > https://lists.linaro.org/mailman/listinfo/lng-odp _______________________________________________ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp