From patchwork Thu Jun 30 08:02:07 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Forrest Shi X-Patchwork-Id: 71223 Delivered-To: patch@linaro.org Received: by 10.140.28.4 with SMTP id 4csp267058qgy; Thu, 30 Jun 2016 01:13:16 -0700 (PDT) X-Received: by 10.55.200.27 with SMTP id c27mr17595550qkj.46.1467274396106; Thu, 30 Jun 2016 01:13:16 -0700 (PDT) Return-Path: Received: from lists.linaro.org (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTP id h10si1929004qth.66.2016.06.30.01.13.15; Thu, 30 Jun 2016 01:13:16 -0700 (PDT) Received-SPF: pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) client-ip=54.225.227.206; Authentication-Results: mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=pass (p=NONE dis=NONE) header.from=linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id 26B8E6841B; Thu, 30 Jun 2016 08:13:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on ip-10-142-244-252 X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAD_ENC_HEADER,BAYES_00, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,URIBL_BLOCKED autolearn=disabled version=3.4.0 Received: from [127.0.0.1] (localhost [127.0.0.1]) by lists.linaro.org (Postfix) with ESMTP id 1AA9367453; Thu, 30 Jun 2016 08:12:55 +0000 (UTC) X-Original-To: lng-odp@lists.linaro.org Delivered-To: lng-odp@lists.linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id B28C367454; Thu, 30 Jun 2016 08:12:50 +0000 (UTC) Received: from na01-bn1-obe.outbound.protection.outlook.com (mail-bn1bon0073.outbound.protection.outlook.com [157.56.111.73]) by lists.linaro.org (Postfix) with ESMTPS id 8DD5867450 for ; Thu, 30 Jun 2016 08:12:47 +0000 (UTC) Received: from BLUPR0301CA0023.namprd03.prod.outlook.com (10.162.113.161) by SN2PR03MB2413.namprd03.prod.outlook.com (10.166.211.6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.528.16; Thu, 30 Jun 2016 08:12:46 +0000 Received: from BN1BFFO11FD010.protection.gbl (2a01:111:f400:7c10::1:188) by BLUPR0301CA0023.outlook.office365.com (2a01:111:e400:5259::33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.528.16 via Frontend Transport; Thu, 30 Jun 2016 08:12:45 +0000 Received-SPF: SoftFail (protection.outlook.com: domain of transitioning linaro.org discourages use of 192.88.158.2 as permitted sender) Received: from az84smr01.freescale.net (192.88.158.2) by BN1BFFO11FD010.mail.protection.outlook.com (10.58.144.73) with Microsoft SMTP Server (TLS) id 15.1.523.9 via Frontend Transport; Thu, 30 Jun 2016 08:12:45 +0000 Received: from titan.ap.freescale.net ([10.192.208.233]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id u5U8Cfmq031448; Thu, 30 Jun 2016 01:12:42 -0700 From: To: Date: Thu, 30 Jun 2016 16:02:07 +0800 Message-ID: <1467273727-11784-1-git-send-email-forrest.shi@linaro.org> X-Mailer: git-send-email 2.1.0.27.g96db324 X-EOPAttributedMessage: 0 X-Matching-Connectors: 131117479658701657; (91ab9b29-cfa4-454e-5278-08d120cd25b8); () X-Forefront-Antispam-Report: CIP:192.88.158.2; IPV:NLI; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(6009001)(7916002)(2980300002)(189002)(199003)(51234002)(81166006)(81156014)(19580395003)(69596002)(19580405001)(8676002)(5003940100001)(586003)(15395725005)(50986999)(87936001)(33646002)(106466001)(2876002)(92566002)(2906002)(7846002)(105596002)(2351001)(86152002)(356003)(4326007)(305945005)(229853001)(86362001)(575784001)(47776003)(104016004)(50226002)(6806005)(77096005)(97736004)(15975445007)(68736007)(110136002)(36756003)(48376002)(11100500001)(189998001)(8936002)(50466002)(21314002)(32563001)(579004); DIR:OUT; SFP:1101; SCL:1; SRVR:SN2PR03MB2413; H:az84smr01.freescale.net; FPR:; SPF:SoftFail; PTR:InfoDomainNonexistent; MX:1; A:1; LANG:en; X-Microsoft-Exchange-Diagnostics: 1; BN1BFFO11FD010; 1:sszKgUF7bkk5ONuC1rAdoz2jEW6PbKnJ6dzqV3eDC54rcZDWNGjxreTAiMJi/yjEeEgLf6GHOhj9uIISc86atevD0aHPd9KC2QrjNBc4sq8WVlLMXAOqHjkAKR9DLIbzGCr1M+5MQ4iUFo0/xC/yR/rPS3W7A/mzG0CCctCkUlY2W6y5H3kybClpU0hQ8Y/Kd9IndxqGQruvDp7xWDf9uXESbnsO4cO9oKC/sKx/scz+uedquG/fBaj/KjVatn4c0XNDiS48skXp6wrzvr4ONKcuy7MrG8g5d2egf44vjFg5z9dOSNdwtXy5DoI7O6BKTQTVHkFEZWaUhhXoezc/CHhQqmzCQjL/DjaT3ND672BHlZNZhY7OfCjzfdMCTmSSN0/9tFFZ9j/tTG839EcQznHR1RsMhpDeg4WC2/H2gZQITH8O7XUcUTROFxROyfhRm3cvPbuzFscgu99RsPcYv33kAfaEnD8DamgLDa7P06JMcv2RmEZWlk0Zm4WnVvCpuQz6eZwri3TGLnKBLAQlbvypQpufHkzcaBbuVTScA4LbjcEPFt8Typv3zn5EFTVWmOu29wYjDy0NOgANdXkAW/MFIC8/IphYfFhjpe7jhOlUrIPeIB8eqcl4i4Ky9j6JkcQqodY/6k9MGANuh7y5XjsTx20oGbggkCYh/ApPwUo= MIME-Version: 1.0 X-MS-Office365-Filtering-Correlation-Id: 209d3394-e6a6-42fd-61f9-08d3a0be515b X-Microsoft-Exchange-Diagnostics: 1; SN2PR03MB2413; 2:nH9OESjEmXJ3XKYsL0wpDf/Jl+WRu4qHKbgmAh49ZB+kPcdXqMlKDzWg6XmAauLsdCAzApHI0KRo7CbLOxbLvQ3EuaampiOCwZD25yyzwdUWD9oDN/W4DUWizDblsqSYLQVC/tVM/pAgnL6Se+5ZOS5T8RQD8f2bTQj0oBCGvtMaYFGtq3aiK1RwIzC+SSST; 3:vgNYuOch2t2VeYsIcOc5wkZ2TvS5mypFeSQAEVfKHkazGCqMAbGgd79h5h98ip0GjdqsrLpDQ3yd6WqP5S7YyKNvMd0t7Pub+5YrUwSprHn8MI6mhmCkywmFyhqiwWOioFFygvAixk1/MJ84ZtBRQNTkILbBanBsjvUaBDQz4a/oa0djRpOwQYV83rIq0W54UFGMMnnCbaC8hrVtBjUORd5BuJOMvuOrKNeJRwU1nmA=; 25:F21mQFPYlhWRDOUVxaC2+y5FaoXSY1geDXAodgJMOOHq5zVVD+A+dl23C3hljBgJtnvVztqpvBIRccbYhCPi18CUx6KmGfn4u50F7gDEkGiYdZMpr/RrVAIG4TbmE3WUG+Z6QNTuSbCgw6s1OAndRNZpRnYaVXjKQ2xn6IxG/urp64eqpnjcTanHx9LYLkSvYxl621RmPVCr10ukyRn+a6qfO0NbYjY7ChDvCjbCt7zqCptiFy7mui1qPUW8TUaAuF95eQPDvPy+1Muv3cczCpLkXVv6nMeV7ZrFUquMcWTXEMwmUX2mET8IOiCAtoT1/TKlN+7mHRyzSUUp+cMVheCaBJ0uwGXuVKsBCC++O1YHFrvw+sfZm2wJnobU0VIJ9iDv3yVYwzppywDhC3baShOCVCGciJ8B/8y3M/wQyFI= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:SN2PR03MB2413; X-Microsoft-Exchange-Diagnostics: 1; SN2PR03MB2413; 31:bpikUZ/9XYMVnFKhX78CR4+wuIDtgcuVysAJqcW5V6Vh+fORGywj+Xrwgy3PQ1tmhkVGVZPz9OstxofReZBkZt51C+wOHNPXUxMg8h1wziXbgot6NhW4paeunh7qTTjjaCXFX3kRXKmaX0US5QQKT71mFjrhDK9O4QNOtkE/m4UGhWrE9R5U3NB89I1G2QfyQyBXVIEADNtjECX8lgJw4w==; 4:uZrG48vz3G8WZ7TNgWI1k95cg8/F82V+K5B9vWxnuUKfer0XfkSuKIQn5H/Mw7gmsYfUNxmx91w9w8aoUXdpSBzjc+YYr7UmB7kVEIWqrK1CEHyuuJYN0P+q7KvmHQ36U19oVzPpY878bvPNogmZxUmJppmoza3yJcl1sn3GbzNDnt5HPtgAJn2nGsBqvOqDpmndd0cD4CI0I/takY6RK3CMWwj62Atb1Pc7c8NCikCxHgS8YYeQibQkzJmwy0qYLMo/gm9YC5emaUKxc97nVNul12x7eyRggNtrPu3CJThquxcfG+YoIbtIxqm9lWyMm2kgHNtQfcmf5NoJvIIakXXEMRL2b9Py74zb5FmuAE1+v2sYP0H30U6+qb/0OZnIafBjeiYdbbvwp9lHJm6Y0Ncryc5EzJTSrtTcxx2YQFOaO+JhhgeavHJIa1lD10Ocj0DlTzHxCPyjgXMg3tTalzXwrRTJ3OZeBtncd/9LiXY= X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(13023025)(13015025)(13018025)(13024025)(13017025)(5005006)(8121501046)(3002001)(10201501046)(6055026); SRVR:SN2PR03MB2413; BCL:0; PCL:0; RULEID:(400006); SRVR:SN2PR03MB2413; X-Forefront-PRVS: 0989A7979C X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; SN2PR03MB2413; 23:PXGFARsjZsJoWJ4uyfyXvjDJnXRjRLjtjeeduinkv?= =?us-ascii?Q?jWfOpyoNgLe0IMHpQlaNfzKslMzdol02bVVJfQ0wfTgp1f8llnLogLBAGuao?= =?us-ascii?Q?I88bpcrqXiFjmqtlNzHiSZqPCnNdQbyFmn8HtIYwoDBzmfFZtd58Z4r/3dxz?= =?us-ascii?Q?jg4npQlowdFl5bShYaTe6SvNVB9oeAH21gmV+tdpcR21RjJq0eLV/cQf3ZxX?= =?us-ascii?Q?kzMrQFHGPABfx2g4HE8R/L7kUqDawdKTIK4b5C76W3xvsiW5F3TlccyblAyq?= =?us-ascii?Q?OArPcHVEEyys6cdNmMUfZZ3/UzdMbTVrL9kipmTDZyVkdad8v5WiN/8xXDk0?= =?us-ascii?Q?k80aMs803LBevumRirDuIcq04kUwD4SBeAXBQTi0JHdvGfj/MIC11zlwi/4e?= =?us-ascii?Q?OeJ8wLMDdn3y9FhR7S/aseecp/iosEKWL7CnG7xdiyD7a9/gBzjuCnKYn8vJ?= =?us-ascii?Q?IpoKRfVP2fQ3nuZTnWvIaBhCR20jiBRr329GZOfjsFkvgMBQl1Qde4j8hHT9?= =?us-ascii?Q?Qtcjr/G8jqzg3jpann/wCsU0xJlVgBOB9gC1Zwbo3k1PswKDyPKsd8BwO+Ma?= =?us-ascii?Q?aXnuRPqmIufUy08vHBmMGsD0oClA+LpWhl4r9KWZMbTnzN7AIbdxBUFm1i32?= =?us-ascii?Q?4oT34tEKyshn+QoRGLxyn5/fr0KTPdAma64KNlsfm8Op7cTmfAkZxm1xrwBB?= =?us-ascii?Q?6au7OxJ5zmZ9QjzXEWX5AxAtbg36kyEh4WDDaD61fzvMM+7AY3V46JJgY4GM?= =?us-ascii?Q?zxzWrEFihzEm94vjoTZzB8FmXHg2WZVfzLd7q91/mLplVX2KqkUtvhz4hNda?= =?us-ascii?Q?LarOZnwP0Egh0osu1T4nP8A6xE+d0VhN2kYz+Z+XxpfGYowMEFqLS2N2d6Zq?= =?us-ascii?Q?ADpvMUxVbGyZQpQs6u3Z1F4J0f2W8Lkxe5jv9h212Dd1vdkTuvuDJ0zFXNXj?= =?us-ascii?Q?mEv4LYxXY2hypqkr7TEFNWdL51LsQ8fd76wK2S3zgT4mXfIGJof8su+X2xXG?= =?us-ascii?Q?2O4hRi8i8ob4fbA/aS+mYwa7Hz0wy65Aow4XIGclMc/GxSw+72AUU3gYOIQ8?= =?us-ascii?Q?i1joBr1hBHBxndzSlX7rnKoDNBvwvvtuyK62PqKKjSR8jTotFGsFGeJirD3O?= =?us-ascii?Q?aKmNgbGLdnn7o+p/9BXu39ZAUZRmGus6zW21Il80DsHXPsGTO16m8L470r5P?= =?us-ascii?Q?EnuTT5yF1YZbVBhqlkYg79bWsuewZFHrg59?= X-Microsoft-Exchange-Diagnostics: 1; SN2PR03MB2413; 6:SExAs4kmvp9GUbEakKri8x7X8Td1gPGtEsQJkW0YVFuL4bGg4liHRPKIuOGpUq7kASjT5Tie+Zv8n4Z97GTLXOsYfKvZ6XuC34fwuSovJ0LHYPuc7YBpcUCujJ5pFAbp3FNg0xeRdYX6QcYfVp5/TMPCwPs/5NoDSfjM9iKQrfdWVn5p6MovTke4FT4WMF84jm3RdQSosofjjYiq2uPMgWF6Z8i8td9d/FoheGWNzSk/CBlI/7QNcBc8ixXm9s3bCfGmPNLkN6bJFz/Oj6iyAFFddeAC74WKJkPuIXfFFNI=; 5:V9hYrqgwQhZxQNqOjepOAwS00+ZDD6gVyAenVEuDESNBKbvlL+FKI74hN39GAg77Dnd0NYReTOCU7kmRJfGvxDG/NXz5On6Hzso8MWeVbg0jJ/d1dLl4LUq4pZ9hhw0SztRvIhrkx4arrTIwI/2T3TDLgnAjiNED6Lp7rnfT5Og=; 24:xABipJMkdBHRnyjfXsrs6B8V3TWD9DYbCfHbhXeohIA8+uONfXIo0cG9GLJXwOQSGJEpzyRkzxwNlFpqo0Lbc5n9KzmzuUJqzkLqXEk33/E=; 7:nuD4mp9lm9+7VQrT9/TsLT2Pc3ccd12F/jCnQIb1tldFqg7yuGAyy2OmKFlRktpIlcQRrQ6vRfV9AtjNmN+MliAFDtwXCN58HtkmBCfRTP+T/AP45gSr8D+pHWx+S33M9yhirieemAKhLpnhvqWl/ilNriNyRJUBdz0/NrmwordIKZ1jf2fGmaGyq2nKKM9bZC5kjEJgGks1RV+cAyF/3P/s8VjSWqaGGf94KVJmmV/lqhEait2JnbqJGpK+pENA SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Jun 2016 08:12:45.6049 (UTC) X-MS-Exchange-CrossTenant-Id: 5afe0b00-7697-4969-b663-5eab37d5f47e X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5afe0b00-7697-4969-b663-5eab37d5f47e; Ip=[192.88.158.2]; Helo=[az84smr01.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN2PR03MB2413 X-Topics: patch Cc: lng-odp@lists.linaro.org Subject: [lng-odp] [PATCH v5] example: introducing l3fwd X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: "The OpenDataPlane \(ODP\) List" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: lng-odp-bounces@lists.linaro.org Sender: "lng-odp" From: Xuelin Shi multi-thread, multi-queues and bi-directional forwarding. support (port, queue, thread) arguments in cmdline which specify how the threads handle which rx queue at which port, if no this argument, default specification used. both hash and lpm based lookup methods are supported, default lpm. Signed-off-by: Xuelin Shi --- change history: v5: address the comments of v4 improve the display info v4: address the comments of v3 add lpm lookup method 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/odp_l3fwd.c | 901 ++++++++++++++++++++++++++++++++++++++++++ example/l3fwd/odp_l3fwd_db.c | 408 +++++++++++++++++++ example/l3fwd/odp_l3fwd_db.h | 122 ++++++ example/l3fwd/odp_l3fwd_lpm.c | 224 +++++++++++ example/l3fwd/odp_l3fwd_lpm.h | 20 + example/m4/configure.m4 | 1 + 9 files changed, 1693 insertions(+), 1 deletion(-) create mode 100644 example/l3fwd/.gitignore create mode 100644 example/l3fwd/Makefile.am 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 create mode 100644 example/l3fwd/odp_l3fwd_lpm.c create mode 100644 example/l3fwd/odp_l3fwd_lpm.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..2eb6b29 --- /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/odp_l3fwd_db.h \ + $(top_srcdir)/example/l3fwd/odp_l3fwd_lpm.h \ + $(top_srcdir)/example/example_debug.h + +dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c odp_l3fwd_lpm.c diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c new file mode 100644 index 0000000..1e77b05 --- /dev/null +++ b/example/l3fwd/odp_l3fwd.c @@ -0,0 +1,901 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "odp_l3fwd_db.h" +#include "odp_l3fwd_lpm.h" + +#define POOL_NUM_PKT 8192 +#define POOL_SEG_LEN 1856 +#define MAX_PKT_BURST 32 + +#define MAX_NB_WORKER 32 +#define MAX_NB_PKTIO 32 +#define MAX_NB_RX_QUEUE (MAX_NB_WORKER) +#define MAX_NB_TX_QUEUE (MAX_NB_WORKER) +#define MAX_NB_QCONF_PER_CORE (MAX_NB_PKTIO) +#define MAX_NB_QCONFS 1024 +#define MAX_NB_ROUTE 32 +#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; + odph_ethaddr_t mac_addr; + odp_pktin_queue_t ifin[MAX_NB_RX_QUEUE]; + odp_pktout_queue_t ifout[MAX_NB_TX_QUEUE]; + int nb_rxq; + int 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; +}; + +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; + int hash_mode; /* 1:hash, 0:lpm */ +} app_args_t; + +struct { + app_args_t cmd_args; + struct l3fwd_pktio_s l3fwd_pktios[MAX_NB_PKTIO]; + odph_odpthread_t l3fwd_workers[MAX_NB_WORKER]; + struct thread_arg_s worker_args[MAX_NB_WORKER]; + + /* forward func, hash or lpm */ + int (*fwd_func)(odp_packet_t pkt, int sif); + odph_ethaddr_t eth_dest_mac[MAX_NB_PKTIO]; +} global; + +static void print_usage(char *progname); +static void print_qconf_table(app_args_t *args); +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 void setup_lpm_fwd_db(void); +static int split_string(char *str, int stringlen, + char **tokens, int maxtokens, char delim); + +static int 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); + return -1; + } + fwd_pktio->pktio = pktio; + + odp_pktin_queue_param_init(&in_queue_param); + odp_pktout_queue_param_init(&out_queue_param); + + /** + * set rx and tx queue number + * if queue number exceeds the max support number, use shared queues + * and distribute the queues by robin-round + */ + + 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; + } + if (!fwd_pktio->nb_rxq) + fwd_pktio->nb_rxq = 1; + if (!fwd_pktio->nb_txq) + fwd_pktio->nb_txq = 1; + + nb_rxq = (int)capa.max_input_queues; + nb_txq = (int)capa.max_output_queues; + if (fwd_pktio->nb_rxq > nb_rxq) { + in_queue_param.op_mode = ODP_PKTIO_OP_MT; + printf("Queue config: max queue %d for %s, share queue used\n", + nb_rxq - 1, name); + } else { + in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE; + nb_rxq = fwd_pktio->nb_rxq; + } + + if (fwd_pktio->nb_txq > nb_txq) { + out_queue_param.op_mode = ODP_PKTIO_OP_MT; + printf("Queue config: max queue %d for %s, share queue used\n", + (int)capa.max_output_queues - 1, name); + } else { + out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE; + nb_txq = fwd_pktio->nb_txq; + } + + in_queue_param.num_queues = nb_rxq; + /* enable flow hashing for multi-input queues */ + if (nb_rxq > 1) { + in_queue_param.hash_enable = 1; + in_queue_param.hash_proto.proto.ipv4_tcp = 1; + in_queue_param.hash_proto.proto.ipv4_udp = 1; + } + if (odp_pktin_queue_config(pktio, &in_queue_param)) { + printf("Failed to config input queue for %s\n", name); + return -1; + } + + out_queue_param.num_queues = nb_txq; + if (odp_pktout_queue_config(pktio, &out_queue_param)) { + printf("Failed to config output queue for %s\n", name); + return -1; + } + + inq = fwd_pktio->ifin; + if (odp_pktin_queue(pktio, inq, nb_rxq) != nb_rxq) { + printf("pktin queue query failed for %s\n", name); + return -1; + } + + outq = fwd_pktio->ifout; + if (odp_pktout_queue(pktio, outq, nb_txq) != nb_txq) { + printf("pktout queue query failed for %s\n", name); + return -1; + } + + if (fwd_pktio->nb_rxq > nb_rxq) { + for (rc = nb_rxq; rc < fwd_pktio->nb_rxq; rc++) + inq[rc] = inq[rc % nb_rxq]; + } + + if (fwd_pktio->nb_txq > nb_txq) { + for (rc = nb_txq; rc < fwd_pktio->nb_txq; rc++) + outq[rc] = outq[rc % nb_txq]; + } + + return 0; +} + +static int find_port_idx_by_name(char *if_name) +{ + int i; + app_args_t *args; + + args = &global.cmd_args; + for (i = 0; i < args->if_count; i++) { + if (!strcmp(args->if_names[i], if_name)) + return i; + } + + return -1; +} + +static void setup_lpm_fwd_db(void) +{ + fwd_db_entry_t *entry; + int if_idx; + + fib_tbl_init(); + + for (entry = fwd_db->list; NULL != entry; entry = entry->next) { + if_idx = find_port_idx_by_name(entry->oif); + if (if_idx < 0) + continue; + + fib_tbl_insert(entry->subnet.addr, if_idx, entry->subnet.depth); + global.eth_dest_mac[if_idx] = entry->src_mac; + } +} + +static int l3fwd_pkt_hash(odp_packet_t pkt, int sif) +{ + fwd_db_entry_t *entry; + ipv4_tuple5_t key; + odph_ethhdr_t *eth; + odph_udphdr_t *udp; + odph_ipv4hdr_t *ip; + uint32_t len; + int dif; + + ip = 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 = odp_packet_l2_ptr(pkt, NULL); + if (entry) { + eth->src = entry->src_mac; + eth->dst = entry->dst_mac; + dif = entry->oif_id; + } else { + /* no route, send by src port */ + eth->dst = eth->src; + dif = sif; + } + + return dif; +} + +static int l3fwd_pkt_lpm(odp_packet_t pkt, int sif) +{ + odph_ipv4hdr_t *ip; + odph_ethhdr_t *eth; + uint32_t len; + int dif; + int ret; + + ip = odp_packet_l3_ptr(pkt, &len); + ip->ttl--; + ip->chksum = odph_ipv4_csum_update(pkt); + eth = odp_packet_l2_ptr(pkt, NULL); + + /* network byte order maybe different from host */ + ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif); + if (ret) + dif = sif; + + eth->dst = global.eth_dest_mac[dif]; + eth->src = global.l3fwd_pktios[dif].mac_addr; + + return dif; +} + +/** + * 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 packets + * @param num Number of packets in pkt_tbl[] + * + * @return Number of packets dropped + */ +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num) +{ + odp_packet_t pkt; + unsigned dropped = 0; + unsigned i, j; + + for (i = 0, j = 0; i < num; ++i) { + pkt = pkt_tbl[i]; + + if (odp_unlikely(odp_packet_has_error(pkt) || + !odp_packet_has_ipv4(pkt))) { + odp_packet_free(pkt); /* Drop */ + dropped++; + } else if (odp_unlikely(i != j++)) { + pkt_tbl[j - 1] = pkt; + } + } + + return dropped; +} + +static void l3fwd_one_queue(uint32_t sif, uint8_t rxq_id) +{ + struct l3fwd_pktio_s *port; + odp_packet_t *tbl; + odp_pktout_queue_t outq; + odp_packet_t pkt_tbl[MAX_PKT_BURST]; + int pkts, drop, sent; + int dif, dst_port; + int i; + + port = &global.l3fwd_pktios[sif]; + pkts = odp_pktin_recv(port->ifin[rxq_id], pkt_tbl, MAX_PKT_BURST); + if (pkts <= 0) + return; + + drop = drop_err_pkts(pkt_tbl, pkts); + pkts -= drop; + + dif = global.fwd_func(pkt_tbl[0], sif); + tbl = &pkt_tbl[0]; + while (pkts) { + dst_port = dif; + for (i = 1; i < pkts; i++) { + dif = global.fwd_func(tbl[i], sif); + if (dif != dst_port) + break; + } + outq = global.l3fwd_pktios[dst_port].ifout[rxq_id]; + sent = odp_pktout_send(outq, tbl, i); + if (odp_unlikely(sent < i)) { + sent = sent < 0 ? 0 : sent; + odp_packet_free_multi(&tbl[sent], i - sent); + } + + if (i < pkts) + tbl += i; + + pkts -= i; + } +} + +static int run_worker(void *arg) +{ + struct thread_arg_s *args = arg; + + for (;;) { + int i; + struct l3fwd_qconf_s *qconf; + + for (i = 0; i < args->count; i++) { + qconf = args->qconf_args[i]; + l3fwd_one_queue(qconf->if_idx, qconf->rxq_idx); + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + odph_odpthread_t thread_tbl[MAX_NB_WORKER]; + odp_pool_t pool; + odp_pool_param_t params; + odp_instance_t instance; + odph_odpthread_params_t thr_params; + odp_cpumask_t cpumask; + int cpu, 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); + } + + /* resolve fwd db*/ + for (i = 0; i < args->if_count; i++) { + struct l3fwd_pktio_s *port; + char *if_name; + + if_name = args->if_names[i]; + port = &global.l3fwd_pktios[i]; + if (create_pktio(if_name, pool, port)) { + printf("Error: create pktio %s\n", if_name); + exit(1); + } + odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN); + resolve_fwd_db(if_name, i, mac); + memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN); + } + + dump_fwd_db(); + print_qconf_table(args); + + /* Decide ip lookup method */ + if (args->hash_mode) { + global.fwd_func = l3fwd_pkt_hash; + } else { + global.fwd_func = l3fwd_pkt_lpm; + setup_lpm_fwd_db(); + } + + /* Start all the available ports */ + for (i = 0; i < args->if_count; i++) { + struct l3fwd_pktio_s *port; + char *if_name; + char buf[32]; + + if_name = args->if_names[i]; + port = &global.l3fwd_pktios[i]; + /* start pktio */ + if (odp_pktio_start(port->pktio)) { + printf("unable to start pktio: %s\n", if_name); + exit(1); + } + + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + port->mac_addr.addr[0], + port->mac_addr.addr[1], + port->mac_addr.addr[2], + port->mac_addr.addr[3], + port->mac_addr.addr[4], + port->mac_addr.addr[5]); + printf("start pktio: %s, mac %s\n", if_name, buf); + } + + 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 = args->worker_count; + memset(thread_tbl, 0, sizeof(thread_tbl)); + /* Create worker threads */ + nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker); + cpu = odp_cpumask_first(&cpumask); + for (i = 0; i < nb_worker; i++) { + struct thread_arg_s *arg; + odp_cpumask_t thr_mask; + + arg = &global.worker_args[i]; + odp_cpumask_zero(&thr_mask); + odp_cpumask_set(&thr_mask, cpu); + thr_params.arg = arg; + odph_odpthreads_create(&thread_tbl[i], &thr_mask, + &thr_params); + cpu = odp_cpumask_next(&cpumask, cpu); + } + + /* wait for other threads to join */ + for (i = 0; i < nb_worker; i++) + odph_odpthreads_join(&thread_tbl[i]); + + 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" + " -s, --style [lpm|hash], ip lookup method\n" + " optional, default as lpm\n" + " -t, --thread Number of threads to do forwarding\n" + " optional, default as availbe worker cpu count\n" + " -q, --queue Configure rx queue(s) for port\n" + " optional, format: [(port, queue, thread),...]\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' */ + {"style", optional_argument, NULL, 's'}, /* return 's' */ + {"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, "+s:t:i:r:q:h", + longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + /* parse ip lookup method */ + case 's': + if (!strcmp(optarg, "hash")) + args->hash_mode = 1; + break; + /* 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; + + p = strchr(p0, '('); + } + 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" + "IP Lookup: %s\n" + "IF Count: %i\n" + "Using IFs: ", + progname, + args->hash_mode ? "hash" : "lpm", + 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 nb_worker; + odp_cpumask_t cpumask; + 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]; + + nb_worker = MAX_NB_WORKER; + if (args->worker_count) + nb_worker = args->worker_count; + + /* Get default worker cpumask */ + nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker); + args->worker_count = nb_worker; + memset(&queue_mask[0][0], 0, sizeof(queue_mask)); + + /* if no queue config specified, generate queue config for each port + * like (0, 0, 0), (1, 0, 1), (2, 0, 2), ... + */ + if (args->qconf_count == 0) { + if (nb_worker > args->if_count) { + printf("Warning: too much threads, " + "truncate to port number: %d\n", args->if_count); + nb_worker = args->if_count; + } + + for (i = 0; i < nb_worker; i++) { + q = &args->qconf_config[i]; + q->core_idx = i; + q->rxq_idx = 0; + q->if_idx = i; + } + + args->qconf_count = nb_worker; + } + + for (i = 0; i < args->qconf_count; i++) { + q = &args->qconf_config[i]; + if (q->core_idx >= args->worker_count || + q->if_idx >= args->if_count) { + printf("Error queue config: max port: %d, " + "max core: %d\n", args->if_count - 1, + args->worker_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; + } +} + +static void print_qconf_table(app_args_t *args) +{ + int i, j; + char buf[32]; + + printf("Rx queue table\n" + "-----------------\n" + "%-16s%-16s%-16s\n", + "port/id", "queue", "thread"); + + for (i = 0; i < args->if_count; i++) { + for (j = 0; j < args->qconf_count; j++) { + struct l3fwd_qconf_s *q; + + q = &args->qconf_config[j]; + if (q->if_idx == i) { + sprintf(buf, "%s/%d", args->if_names[q->if_idx], + q->if_idx); + printf("%-16s%-16d%-16d\n", buf, q->rxq_idx, + q->core_idx); + } + } + } + printf("\n"); + fflush(NULL); +} diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c new file mode 100644 index 0000000..1e3b6e8 --- /dev/null +++ b/example/l3fwd/odp_l3fwd_db.c @@ -0,0 +1,408 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + +#include +#include +#include + +/** Jenkins hash support. + * + * Copyright (C) 2006 Bob Jenkins (bob_jenkins@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$ + */ +#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; \ +} + +/** + * 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, host endianness + * @param depth Pointer to subnet bit width + * @return 0 if successful else -1 + */ +static inline +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *depth) +{ + int b[4]; + int qualifier = 32; + int converted; + uint32_t addr_le; + + 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_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24; + *addr = odp_le_to_cpu_32(addr_le); + *depth = qualifier; + + 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) +{ + sprintf(b, "%d.%d.%d.%d/%d", + 0xFF & ((range->addr) >> 24), + 0xFF & ((range->addr) >> 16), + 0xFF & ((range->addr) >> 8), + 0xFF & ((range->addr) >> 0), + range->depth); + 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, odph_ethaddr_t *mac) +{ + uint8_t *byte; + + byte = mac->addr; + sprintf(b, "%02X.%02X.%02X.%02X.%02X.%02X", + byte[0], byte[1], byte[2], byte[3], byte[4], byte[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.depth); + break; + case 1: + strncpy(entry->oif, token, OIF_LEN - 1); + entry->oif[OIF_LEN - 1] = 0; + break; + case 2: + odph_eth_addr_parse(&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, int portid, 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->oif_id = portid; + memcpy(entry->src_mac.addr, 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("%-16s%-16s%-16s\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("Routing table\n" + "-----------------\n" + "%-16s%-16s%-16s\n", + "subnet", "next_hop", "dest_mac"); + + for (entry = fwd_db->list; NULL != entry; entry = entry->next) + dump_fwd_db_entry(entry); + + printf("\n"); +} + +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) { + uint32_t mask; + + mask = (1u << (32 - entry->subnet.depth)) - 1; + if (entry->subnet.addr == (key->dst_ip & 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..b11a0dc --- /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 +#include + +#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, host endianness */ + uint32_t depth; /**< subnet bit width */ +} 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 */ + int oif_id; /**< Output interface idx */ + odph_ethaddr_t src_mac; /**< Output source MAC */ + odph_ethaddr_t dst_mac; /**< 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 portid Output queue for packet transmit + * @param mac MAC address of this interface + */ +void resolve_fwd_db(char *intf, int portid, 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/l3fwd/odp_l3fwd_lpm.c b/example/l3fwd/odp_l3fwd_lpm.c new file mode 100644 index 0000000..1b3bfcf --- /dev/null +++ b/example/l3fwd/odp_l3fwd_lpm.c @@ -0,0 +1,224 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + +#include +#include + +#include + +/** + * This is a simple implementation of lpm based on patricia tree. + * + * Tradeoff exists between memory consumption and lookup time. + * Currently it prefers 5 levels: {16, 4, 4, 4, 4}, could be 3 + * levels: {16, 8, 8} by defining FIB_NEXT_STRIDE as 8. Other + * levels are also possible. + * + * the ip here is host endian, when doing init or lookup, the + * caller should do endianness conversion if needed. + */ + +#define FIB_IP_WIDTH 32 +#define FIB_FIRST_STRIDE 16 +#define FIB_NEXT_STRIDE 4 +#define FIB_NEXT_SIZE (1 << FIB_NEXT_STRIDE) +#define FIB_SUB_COUNT 16384 +#define DEPTH_TO_MASK(depth) ((1 << (depth)) - 1) + +typedef struct fib_node_s { + union { + uint32_t next_hop; + struct fib_node_s *next; /* next level table */ + }; + uint8_t valid :1; /* 1, this node has a valid next hop */ + uint8_t end :1; /* 0, next points to the extended table */ + uint8_t depth :6; /* bit length of subnet mask */ +} fib_node_t; + +typedef struct fib_sub_tbl_s { + fib_node_t *fib_nodes; + uint32_t fib_count; + uint32_t fib_idx; +} fib_sub_tbl_t; + +static fib_node_t fib_rt_tbl[1 << FIB_FIRST_STRIDE]; +static fib_sub_tbl_t fib_lpm_cache; + +static inline fib_node_t *fib_alloc_sub(void) +{ + fib_node_t *sub_tbl = NULL; + uint32_t i, nb_entry; + + /* extend to next level */ + if (fib_lpm_cache.fib_idx < fib_lpm_cache.fib_count) { + nb_entry = (fib_lpm_cache.fib_idx + 1) * FIB_NEXT_SIZE; + sub_tbl = &fib_lpm_cache.fib_nodes[nb_entry]; + fib_lpm_cache.fib_idx++; + for (i = 0; i < nb_entry; i++) { + sub_tbl[i].valid = 0; + sub_tbl[i].end = 1; + } + } + + return sub_tbl; +} + +static void fib_update_node(fib_node_t *fe, int port, int depth) +{ + fib_node_t *p; + int i; + + if (fe->end) { + if (!fe->valid) { + fe->depth = depth; + fe->next_hop = port; + fe->valid = 1; + } else if (fe->depth <= depth) { + fe->next_hop = port; + fe->depth = depth; + } + + return; + } + + for (i = 0; i < FIB_NEXT_SIZE; i++) { + p = &fe->next[i]; + if (p->end) + fib_update_node(p, port, depth); + } +} + +static void fib_insert_node(fib_node_t *fe, uint32_t ip, uint32_t next_hop, + int ip_width, int eat_bits, int depth) +{ + int i; + uint32_t idx, port; + fib_node_t *p; + + if (fe->end) { + port = fe->next_hop; + p = fib_alloc_sub(); + if (!p) + return; + + fe->next = p; + fe->end = 0; + if (fe->valid) { + for (i = 0; i < FIB_NEXT_SIZE; i++) { + p = &fe->next[i]; + p->next_hop = port; + p->depth = fe->depth; + } + } + } + if (depth - eat_bits <= FIB_NEXT_STRIDE) { + ip_width -= depth - eat_bits; + idx = ip >> ip_width; + ip &= DEPTH_TO_MASK(ip_width); + p = &fe->next[idx]; + fib_update_node(p, next_hop, depth); + } else { + ip_width -= FIB_NEXT_STRIDE; + idx = ip >> ip_width; + p = &fe->next[idx]; + ip &= DEPTH_TO_MASK(ip_width); + eat_bits += FIB_NEXT_STRIDE; + fib_insert_node(p, ip, next_hop, ip_width, eat_bits, depth); + } +} + +void fib_tbl_init(void) +{ + int i; + fib_node_t *fe; + uint32_t size; + odp_shm_t lpm_shm; + + for (i = 0; i < (1 << FIB_FIRST_STRIDE); i++) { + fe = &fib_rt_tbl[i]; + fe->valid = 0; + fe->end = 1; + fe->depth = 0; + fe->next_hop = 0; + } + + size = FIB_NEXT_SIZE * FIB_SUB_COUNT; + /*Reserve memory for Routing hash table*/ + lpm_shm = odp_shm_reserve("fib_lpm_sub", size, ODP_CACHE_LINE_SIZE, 0); + fe = odp_shm_addr(lpm_shm); + if (!fe) { + EXAMPLE_ERR("Error: shared mem alloc failed for lpm cache.\n"); + exit(-1); + } + + fib_lpm_cache.fib_nodes = fe; + fib_lpm_cache.fib_count = FIB_SUB_COUNT; + fib_lpm_cache.fib_idx = 0; +} + +void fib_tbl_insert(uint32_t ip, int port, int depth) +{ + fib_node_t *fe, *p; + uint32_t idx; + int i, j; + int nb_bits; + + nb_bits = FIB_FIRST_STRIDE; + idx = ip >> nb_bits; + fe = &fib_rt_tbl[idx]; + if (depth <= nb_bits) { + if (fe->end) { + fe->next_hop = port; + fe->depth = depth; + fe->valid = 1; + return; + } + + for (i = 0; i < FIB_NEXT_SIZE; i++) { + p = &fe->next[i]; + if (p->end) + fib_update_node(p, port, depth); + else + for (j = 0; j < FIB_NEXT_SIZE; j++) + fib_update_node(&p->next[j], port, + depth); + } + + return; + } + + /* need to check sub table */ + ip &= DEPTH_TO_MASK(FIB_IP_WIDTH - nb_bits); + fib_insert_node(fe, ip, port, FIB_IP_WIDTH - nb_bits, nb_bits, depth); +} + +int fib_tbl_lookup(uint32_t ip, int *port) +{ + fib_node_t *fe; + uint32_t idx; + int nb_bits; + + nb_bits = FIB_IP_WIDTH - FIB_FIRST_STRIDE; + idx = ip >> nb_bits; + fe = &fib_rt_tbl[idx]; + + ip &= DEPTH_TO_MASK(nb_bits); + while (!fe->end) { + nb_bits -= FIB_NEXT_STRIDE; + idx = ip >> nb_bits; + fe = &fe->next[idx]; + ip &= DEPTH_TO_MASK(nb_bits); + } + *port = fe->next_hop; + + return fe->valid ? 0 : -1; +} diff --git a/example/l3fwd/odp_l3fwd_lpm.h b/example/l3fwd/odp_l3fwd_lpm.h new file mode 100644 index 0000000..8f78e39 --- /dev/null +++ b/example/l3fwd/odp_l3fwd_lpm.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ODP_L3FWD_LPM_H_ +#define ODP_L3FWD_LPM_H_ + +#ifdef __cplusplus +extern "C" { +#endif +void fib_tbl_init(void); +void fib_tbl_insert(uint32_t ip, int port, int depth); +int fib_tbl_lookup(uint32_t ip, int *port); +#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])