diff options
author | Ciprian Barbu <ciprian.barbu@linaro.org> | 2014-02-11 14:34:30 +0100 |
---|---|---|
committer | Maxim Uvarov <maxim.uvarov@linaro.org> | 2014-02-12 14:47:49 +0400 |
commit | 78ff37a1a24b16aaad2dfb2f09db6874e9307df8 (patch) | |
tree | 1cb2089f15f1292c581237a402a97d7c86b29189 | |
parent | 240c87f9f39df823bdbafbc3e893888318ad5096 (diff) |
Netmap pktio example
Signed-off-by: Ciprian Barbu <ciprian.barbu@linaro.org>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.inc | 3 | ||||
-rw-r--r-- | test/Makefile | 3 | ||||
-rw-r--r-- | test/packet_netmap/Makefile | 66 | ||||
-rw-r--r-- | test/packet_netmap/odp_example_pktio_netmap.c | 534 |
5 files changed, 606 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore index 27c81317f..693671dd5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ lib/ obj/ test/example/odp_example test/packet/odp_packet +test/packet_netmap/odp_packet test/api_test/odp_atomic test/api_test/odp_shm test/api_test/odp_ring diff --git a/Makefile.inc b/Makefile.inc index 113efa83f..a0872f6a7 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -5,7 +5,8 @@ PLATFORM ?= linux-generic CFLAGS += -DODP_DEBUG=1 -CFLAGS += -O3 +#CFLAGS += -O3 +CFLAGS += -O0 -g OBJ_DIR = ./obj ODP_LIB = $(ODP_ROOT)/platform/$(PLATFORM) diff --git a/test/Makefile b/test/Makefile index 6cead72c3..2ff7a4c46 100644 --- a/test/Makefile +++ b/test/Makefile @@ -8,15 +8,18 @@ all: $(MAKE) -C api_test $(MAKE) -C example $(MAKE) -C packet + $(MAKE) -C packet_netmap .PHONY: clean clean: $(MAKE) -C api_test clean $(MAKE) -C example clean $(MAKE) -C packet clean + $(MAKE) -C packet_netmap clean .PHONY: install install: $(MAKE) -C api_test install $(MAKE) -C example install $(MAKE) -C packet install + $(MAKE) -C packet_netmap install diff --git a/test/packet_netmap/Makefile b/test/packet_netmap/Makefile new file mode 100644 index 000000000..4deca3557 --- /dev/null +++ b/test/packet_netmap/Makefile @@ -0,0 +1,66 @@ +# Copyright (c) 2013, Linaro Limited +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +ODP_ROOT = ../.. +ODP_APP = odp_packet + +CFLAGS += -I. +CFLAGS += -I$(ARCH_INC) +CFLAGS += -DODP_HAVE_NETMAP +CFLAGS += -O0 -g + +include $(ODP_ROOT)/Makefile.inc + +LDFLAGS += -lrt +LDFLAGS += $(EXTRA_CFLAGS) + +OBJS = +OBJS += $(OBJ_DIR)/odp_example_pktio_netmap.o + +DEPS = $(OBJS:.o=.d) + +.PHONY: all +all: $(OBJ_DIR) $(ODP_APP) + +-include $(DEPS) + +$(OBJ_DIR): + mkdir $(OBJ_DIR) + +$(LIB): + @echo Building $@ + $(MAKE) -C $(ODP_LIB) libs + +# +# Compile rules +# +$(OBJ_DIR)/%.o: %.c + @echo Compiling $< + $(CC) -c -MD $(CFLAGS) -o $@ $< + +# +# Link rule +# +$(ODP_APP): $(LIB) $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LIB) -o $@ + +.PHONY: libs +libs: + $(MAKE) -C $(ODP_LIB) libs + +.PHONY: docs +docs: + $(MAKE) -C $(ODP_LIB) docs + +.PHONY: clean +clean: + rm -rf $(OBJ_DIR) + rm -f $(ODP_APP) + $(MAKE) -C $(ODP_LIB) clean + +.PHONY: install +install: + install -d $(DESTDIR)$(prefix)/usr/local/share/odp + install -m 0755 $(ODP_APP) $(DESTDIR)$(prefix)/usr/local/share/odp/ diff --git a/test/packet_netmap/odp_example_pktio_netmap.c b/test/packet_netmap/odp_example_pktio_netmap.c new file mode 100644 index 000000000..aa38f2a94 --- /dev/null +++ b/test/packet_netmap/odp_example_pktio_netmap.c @@ -0,0 +1,534 @@ +/* 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 <odp_debug.h> +#include <helper/odp_linux.h> +#include <helper/odp_eth.h> +#include <helper/odp_ip.h> + +#include <odp_pktio_netmap.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 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; /**< Related pktio_entry */ +} 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]; + /** Lookup table */ + unsigned char pktio_tbl[ODP_CONFIG_PKTIO_ENTRIES]; +} args_t; + +/** Global pointer to args */ +static args_t *args; + +/* helper funcs */ +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; + thread_args_t *thr_args; + odp_packet_t pkt; + odp_buffer_t buf; + unsigned long pkt_cnt = 0; + + 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) { + 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; + int pktio_nr; + + /* Use schedule to get buf from any input queue */ + buf = odp_schedule_poll(NULL); + + pkt = odp_packet_from_buffer(buf); + 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_nr = args->pktio_tbl[pktio_tmp]; + odp_queue_enq(args->thread[pktio_nr].bridge_q, buf); + + /* Send back packets arrived on physical interface */ + if (args->thread[pktio_nr].netmap_mode == ODP_NETMAP_MODE_HW) { + odp_packet_t pkt_copy; + odp_buffer_t buf_copy; + size_t frame_len = odp_packet_get_len(pkt); + size_t l2_offset = odp_packet_l2_offset(pkt); + size_t l3_offset = odp_packet_l3_offset(pkt); + + buf_copy = odp_buffer_alloc(pkt_pool); + pkt_copy = odp_packet_from_buffer(buf_copy); + + odp_packet_init(pkt_copy); + odp_packet_set_len(pkt_copy, frame_len); + odp_packet_set_l2_offset(pkt_copy, l2_offset); + odp_packet_set_l3_offset(pkt_copy, l3_offset); + + memcpy(odp_buffer_addr(pkt_copy), + odp_buffer_addr(pkt), frame_len); + + swap_pkt_addrs(&pkt_copy, 1); + + buf_copy = odp_buffer_from_packet(pkt_copy); + odp_queue_enq(outq_def, buf_copy); + } + + /* 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); + } + } + + 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; + + /* 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); + + /* Create and init worker threads */ + memset(thread_tbl, 0, sizeof(thread_tbl)); + for (i = 0; i < num_workers; ++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; + int if_idx; + + if (i == 2) + break; + + /* In netmap mode there will be one thread polling the physical + * interface and one polling the host stack for that interface + */ + if_idx = i < 2 * args->appl.if_count ? i / 2 : -1; + + if (if_idx == -1) { + args->thread[i].pktio_dev = NULL; + continue; + } + args->thread[i].pktio_dev = args->appl.ifs[if_idx].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->thread[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->thread[i].pktio = pktio; + /* Save pktio id in the lookup table */ + args->pktio_tbl[pktio] = 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->thread[i-1].pktio; + outq_def = odp_pktio_outq_getdef(pktio_bridge); + args->thread[i].bridge_q = outq_def; + + pktio_bridge = args->thread[i].pktio; + outq_def = odp_pktio_outq_getdef(pktio_bridge); + args->thread[i-1].bridge_q = outq_def; + } + + args->thread[i].pool = pool; + } + + for (i = 0; i < num_workers; ++i) { + if (i == 2) + break; + + /* + * 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, + &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; +} + +/** + * 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]; + eth = (odp_ethhdr_t *)odp_packet_l2(pkt); + + if (odp_be_to_cpu_16(eth->type) == ODP_ETHTYPE_IPV4) { + tmp_addr = eth->dst; + eth->dst = eth->src; + eth->src = tmp_addr; + + /* 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) + ); +} |