From patchwork Thu May 15 09:22:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Venkatesh Vivekanandan X-Patchwork-Id: 30227 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ve0-f200.google.com (mail-ve0-f200.google.com [209.85.128.200]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 237D820446 for ; Thu, 15 May 2014 09:23:14 +0000 (UTC) Received: by mail-ve0-f200.google.com with SMTP id pa12sf3181862veb.3 for ; Thu, 15 May 2014 02:23:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:date:message-id :mime-version:subject:precedence:list-id:list-unsubscribe :list-archive:list-post:list-help:list-subscribe:errors-to:sender :x-original-sender:x-original-authentication-results:mailing-list :content-type:content-transfer-encoding; bh=2Wu0YB891CSV9RSgvj7B91IQd7/MsRX8FIMRQJy+29M=; b=ezSgc1y5zB5dQegyjYqPL9aNAlRoQ38LDGzq77SWPyCno5es22WYitQVmARVm/wflp leX/PymmMDQ3Go3HNeR/S3QPu3Bv0EYx1ju3H8MrTx2xHdzwNIgRqJwwOcBk1KWywA/Y LMCwUo05aVVkDUmWLUgANuCqrh+uv/7kYB4cVtnZkaYSfWro5N0Si/92A3ajsFtd5A/Z lejiglb+eq0jAzZcksw7oPFkHbLzKJIWB0LHWSFCjanp/YMAF1ZhndKwrlLhy0Ivqbr0 4ECPsghqJmn2lW/vlpXtDRK8IAXNoq70JtbJs0D7xwJ8xsY8NiPwbWUFqL4kwGj5dD5k 5bQw== X-Gm-Message-State: ALoCoQngyoDNIokUEfkez4pphu/8yOd8Wd76FM8/JldsdK3Dzm0oJhKDq5j53oRrz+5c+3RpKwgP X-Received: by 10.58.127.66 with SMTP id ne2mr4259524veb.27.1400145793961; Thu, 15 May 2014 02:23:13 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.82.145 with SMTP id h17ls155604qgd.31.gmail; Thu, 15 May 2014 02:23:13 -0700 (PDT) X-Received: by 10.52.189.97 with SMTP id gh1mr6504804vdc.0.1400145793811; Thu, 15 May 2014 02:23:13 -0700 (PDT) Received: from mail-vc0-f182.google.com (mail-vc0-f182.google.com [209.85.220.182]) by mx.google.com with ESMTPS id tx4si831187vdc.40.2014.05.15.02.23.13 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 15 May 2014 02:23:13 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.182 as permitted sender) client-ip=209.85.220.182; Received: by mail-vc0-f182.google.com with SMTP id la4so3962291vcb.41 for ; Thu, 15 May 2014 02:23:13 -0700 (PDT) X-Received: by 10.52.137.174 with SMTP id qj14mr6415124vdb.32.1400145793506; Thu, 15 May 2014 02:23:13 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.221.72 with SMTP id ib8csp306995vcb; Thu, 15 May 2014 02:23:13 -0700 (PDT) X-Received: by 10.140.23.166 with SMTP id 35mr12744572qgp.89.1400145792764; Thu, 15 May 2014 02:23:12 -0700 (PDT) Received: from ip-10-141-164-156.ec2.internal (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTPS id k9si2231331qan.127.2014.05.15.02.23.12 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 15 May 2014 02:23:12 -0700 (PDT) Received-SPF: none (google.com: lng-odp-bounces@lists.linaro.org does not designate permitted sender hosts) client-ip=54.225.227.206; Received: from localhost ([127.0.0.1] helo=ip-10-141-164-156.ec2.internal) by ip-10-141-164-156.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1Wkrrm-00067X-Pv; Thu, 15 May 2014 09:22:18 +0000 Received: from mail-gw2-out.broadcom.com ([216.31.210.63]) by ip-10-141-164-156.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1Wkrrg-00067P-0C for lng-odp@lists.linaro.org; Thu, 15 May 2014 09:22:12 +0000 X-IronPort-AV: E=Sophos;i="4.97,1057,1389772800"; d="scan'208";a="29476896" Received: from irvexchcas07.broadcom.com (HELO IRVEXCHCAS07.corp.ad.broadcom.com) ([10.9.208.55]) by mail-gw2-out.broadcom.com with ESMTP; 15 May 2014 02:50:38 -0700 Received: from IRVEXCHSMTP2.corp.ad.broadcom.com (10.9.207.52) by IRVEXCHCAS07.corp.ad.broadcom.com (10.9.208.55) with Microsoft SMTP Server (TLS) id 14.3.174.1; Thu, 15 May 2014 02:22:58 -0700 Received: from mail-irva-13.broadcom.com (10.10.10.20) by IRVEXCHSMTP2.corp.ad.broadcom.com (10.9.207.52) with Microsoft SMTP Server id 14.3.174.1; Thu, 15 May 2014 02:22:58 -0700 Received: from localhost.localdomain (unknown [10.131.60.56]) by mail-irva-13.broadcom.com (Postfix) with ESMTP id 6C8929FA0A; Thu, 15 May 2014 02:22:57 -0700 (PDT) From: To: Date: Thu, 15 May 2014 14:52:31 +0530 Message-ID: <1400145751-28258-1-git-send-email-venkatesh.vivekanandan@linaro.org> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 X-Topics: patch Subject: [lng-odp] [PATCHv2] L2 Forwarding App X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , Errors-To: lng-odp-bounces@lists.linaro.org Sender: lng-odp-bounces@lists.linaro.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: venkatesh.vivekanandan@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.182 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 From: Venkatesh Vivekanandan Initial L2 forwarding application based on example pktio that resembles DPDK's in functionality. It is based on raw sockets and not tested on any network HW accelerator. Create pktio and queue for all threads before starting pthread. It has two modes supported, 1. burst mode (without ODP queues) 2. queue mode (with ODP queues and scheduler) and three types, 1. raw sockets 2. multiple messages on sockets 3. mmap on sockets Signed-off-by: Venkatesh Vivekanandan --- test/Makefile | 3 + test/l2fwd/Makefile | 44 ++++ test/l2fwd/l2fwd.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 696 insertions(+) create mode 100644 test/l2fwd/Makefile create mode 100644 test/l2fwd/l2fwd.c diff --git a/test/Makefile b/test/Makefile index cc3f4e9..29931c1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -11,6 +11,7 @@ all: $(MAKE) -C packet_netmap $(MAKE) -C timer $(MAKE) -C generator + $(MAKE) -C l2fwd .PHONY: clean clean: @@ -20,6 +21,7 @@ clean: $(MAKE) -C packet_netmap clean $(MAKE) -C timer clean $(MAKE) -C generator clean + $(MAKE) -C l2fwd clean .PHONY: install install: @@ -29,3 +31,4 @@ install: $(MAKE) -C packet_netmap install $(MAKE) -C timer install $(MAKE) -C generator install + $(MAKE) -C l2fwd install diff --git a/test/l2fwd/Makefile b/test/l2fwd/Makefile new file mode 100644 index 0000000..3435caa --- /dev/null +++ b/test/l2fwd/Makefile @@ -0,0 +1,44 @@ +# Copyright (c) 2014, Linaro Limited +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +ODP_ROOT = ../.. +ODP_APP = l2fwd + +include $(ODP_ROOT)/Makefile.inc +include ../Makefile.inc + +OBJS = +OBJS += $(OBJ_DIR)/l2fwd.o + +DEPS = $(OBJS:.o=.d) + +.PHONY: default +default: $(OBJ_DIR) $(ODP_APP) + +-include $(DEPS) + +# +# Compile rules +# +$(OBJ_DIR)/%.o: %.c + $(ECHO) Compiling $< + $(CC) -c -MD $(EXTRA_CFLAGS) $(CFLAGS) -o $@ $< + +# +# Link rule +# +$(ODP_APP): $(ODP_LIB) $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(ODP_LIB) $(STD_LIBS) -o $@ + +.PHONY: clean +clean: + $(RMDIR) $(OBJ_DIR) + $(RM) $(ODP_APP) + $(MAKE) -C $(ODP_DIR) clean + +.PHONY: install +install: + install -d $(DESTDIR)/share/odp + install -m 0755 $(ODP_APP) $(DESTDIR)/share/odp/ diff --git a/test/l2fwd/l2fwd.c b/test/l2fwd/l2fwd.c new file mode 100644 index 0000000..956aa36 --- /dev/null +++ b/test/l2fwd/l2fwd.c @@ -0,0 +1,649 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example l2fwd.c ODP basic forwarding application + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 *args; +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, int 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, int 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] = 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 */ + 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); + + if (num_workers < args->appl.if_count) { + ODP_ERR("Error: core count %d is less than interface count\n", + num_workers); + exit(EXIT_FAILURE); + } + if (args->appl.if_count % 2 != 0) { + ODP_ERR("Error: interface count %d is odd in fwd appl.\n", + 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 % args->appl.if_count; + + args->thread[i].srcif = args->appl.if_names[if_idx]; + if (if_idx % 2 == 0) + args->thread[i].dstif = args->appl.if_names[if_idx+1]; + else + args->thread[i].dstif = args->appl.if_names[if_idx-1]; + 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) { + pktio = burst_mode_init_params(&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(&args->thread[i], pool); + if (pktio == ODP_PKTIO_INVALID) { + ODP_ERR(" for thread:%02i\n", i); + exit(EXIT_FAILURE); + } + } + args->thread[i].srcpktio = pktio; + } + for (i = 0; i < num_workers; ++i) { + if (i % 2 == 0) + args->thread[i].dstpktio = args->thread[i+1].srcpktio; + else + args->thread[i].dstpktio = 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 (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, + &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] = 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", optional_argument, NULL, 'c'}, /* return 'c' */ + {"interface", required_argument, NULL, 'i'}, /* return 'i' */ + {"mode", required_argument, NULL, 'm'}, /* return 'm' */ + {"type", optional_argument, NULL, 't'}, /* return 't' */ + {"fanout", optional_argument, NULL, 'f'}, /* return 'f' */ + {"help", no_argument, NULL, 'h'}, /* return 'h' */ + {NULL, 0, NULL, 0} + }; + + appl_args->mode = -1; /* Invalid, must be changed by parsing */ + appl_args->type = 1; /* 1: ODP_PKTIO_TYPE_SOCKET_BASIC */ + appl_args->fanout = 1; /* turn on fanout by default */ + + 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 if (i == 1) { + appl_args->mode = APPL_MODE_PKT_QUEUE; + } else { + usage(argv[0]); + exit(EXIT_SUCCESS); + } + 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" + "\n" + " E.g. ./%s -i eth0,eth1,eth2,eth3 -m 0\n" + " E.g. ./%s -i eth0,eth1,eth2,eth3 -m 1 -c 4 -t 3 -f 0\n" + "\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" + "\n" + "Optional OPTIONS\n" + " -c, --count Core count.\n" + " -t, --type \n" + " 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: 1: ODP_PKTIO_TYPE_SOCKET_BASIC\n" + " -f, --fanout \n" + " 0: off\n" + " 1: on (Default 1: on)\n" + " -h, --help Display help and exit.\n\n" + "\n", NO_PATH(progname), NO_PATH(progname), NO_PATH(progname) + ); +}