From patchwork Fri Oct 16 08:55:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gregory Etelson X-Patchwork-Id: 81064 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 312EFA04DB; Fri, 16 Oct 2020 10:56:39 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 2DE7C1E9AD; Fri, 16 Oct 2020 10:56:28 +0200 (CEST) Received: from hqnvemgate26.nvidia.com (hqnvemgate26.nvidia.com [216.228.121.65]) by dpdk.org (Postfix) with ESMTP id C711C1E91C for ; Fri, 16 Oct 2020 10:56:25 +0200 (CEST) Received: from hqmail.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate26.nvidia.com (using TLS: TLSv1.2, AES256-SHA) id ; Fri, 16 Oct 2020 01:56:11 -0700 Received: from nvidia.com (10.124.1.5) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 16 Oct 2020 08:56:15 +0000 From: Gregory Etelson To: CC: , , , , , , , Ori Kam , Viacheslav Ovsiienko , Thomas Monjalon , Ferruh Yigit , Andrew Rybchenko Date: Fri, 16 Oct 2020 11:55:55 +0300 Message-ID: <20201016085557.18884-2-getelson@nvidia.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201016085557.18884-1-getelson@nvidia.com> References: <20200625160348.26220-1-getelson@mellanox.com> <20201016085557.18884-1-getelson@nvidia.com> MIME-Version: 1.0 X-Originating-IP: [10.124.1.5] X-ClientProxiedBy: HQMAIL111.nvidia.com (172.20.187.18) To HQMAIL107.nvidia.com (172.20.187.13) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1602838571; bh=/6jVazrlMnsgAfIysc1U/qyvzCMaURzmwEdLNyZNK4s=; h=From:To:CC:Subject:Date:Message-ID:X-Mailer:In-Reply-To: References:MIME-Version:Content-Transfer-Encoding:Content-Type: X-Originating-IP:X-ClientProxiedBy; b=YNMqiMu0foyXefAcojngGb7RR3XaevWpQvR05vH1GlzisT+5Z+bO/06GEsSTyxHea 8Nza9ZL1s+/B+Big5t5yADQJzwZMce+sfXkg7gja3K1dDCGKkC2czHi146iqIDV6zE 8nbwvgVzDiK1qoF5VgqvqD0051AQlOGXUV/mpKb7SWa68NT3PIfBVGafh/PawivAw6 +LvYjQKpGz96vYaaVWww1qrgJ6LhMUIi0VF7vb1bBwHx9ocl+tS4aj8fQq/a6X8scy cvrJdQh0JsDMLO5wko1nVbNlGElszLSA+p9joZNzdly33Ge/VEmQsWHh/9SJv+PtS3 OY77L54bbOo5Q== Subject: [dpdk-dev] [PATCH v6 1/3] ethdev: allow negative values in flow rule types X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" RTE flow items & actions use positive values in item & action type. Negative values are reserved for PMD private types. PMD items & actions usually are not exposed to application and are not used to create RTE flows. The patch allows applications with access to PMD flow items & actions ability to integrate RTE and PMD items & actions and use them to create flow rule. RTE flow item or action conversion library accepts positive known element types with predefined sizes only. Private PMD items and actions do not fit into this scheme because PMD type values are negative, each PMD has it's own types numeration and element types and their sizes are not visible at RTE level. To resolve these limitations the patch proposes this solution: 1. PMD can expose elements of pointer size only. RTE flow conversion functions will use pointer size for each configuration object in private PMD element it processes; 2. RTE flow verification will not reject elements with negative type. Signed-off-by: Gregory Etelson Acked-by: Ori Kam Acked-by: Viacheslav Ovsiienko --- v4: * update the 'Negative types' section in the rtre_flow.rst * update the patch comment v5: * rebase to next-net --- doc/guides/prog_guide/rte_flow.rst | 3 +++ doc/guides/rel_notes/release_20_11.rst | 5 +++++ lib/librte_ethdev/rte_flow.c | 28 ++++++++++++++++++++------ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst index 6ee0d3a10a..7fb5ec9059 100644 --- a/doc/guides/prog_guide/rte_flow.rst +++ b/doc/guides/prog_guide/rte_flow.rst @@ -2775,6 +2775,9 @@ identifiers they are not aware of. A method to generate them remains to be defined. +Application may use PMD dynamic items or actions in flow rules. In that case +size of configuration object in dynamic element must be a pointer size. + Planned types ~~~~~~~~~~~~~ diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst index 79d9ebac4e..9155b468d6 100644 --- a/doc/guides/rel_notes/release_20_11.rst +++ b/doc/guides/rel_notes/release_20_11.rst @@ -116,6 +116,11 @@ New Features * Updated HWRM structures to 1.10.1.70 version. * Added TRUFLOW support for Stingray devices. +* **Flow rules allowed to use private PMD items / actions.** + + * Flow rule verification was updated to accept private PMD + items and actions. + * **Updated Cisco enic driver.** * Added support for VF representors with single-queue Tx/Rx and flow API diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c index 686fe40eaa..b74ea5593a 100644 --- a/lib/librte_ethdev/rte_flow.c +++ b/lib/librte_ethdev/rte_flow.c @@ -518,7 +518,11 @@ rte_flow_conv_item_spec(void *buf, const size_t size, } break; default: - off = rte_flow_desc_item[item->type].size; + /** + * allow PMD private flow item + */ + off = (int)item->type >= 0 ? + rte_flow_desc_item[item->type].size : sizeof(void *); rte_memcpy(buf, data, (size > off ? off : size)); break; } @@ -621,7 +625,11 @@ rte_flow_conv_action_conf(void *buf, const size_t size, } break; default: - off = rte_flow_desc_action[action->type].size; + /** + * allow PMD private flow action + */ + off = (int)action->type >= 0 ? + rte_flow_desc_action[action->type].size : sizeof(void *); rte_memcpy(buf, action->conf, (size > off ? off : size)); break; } @@ -663,8 +671,12 @@ rte_flow_conv_pattern(struct rte_flow_item *dst, unsigned int i; for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) { - if ((size_t)src->type >= RTE_DIM(rte_flow_desc_item) || - !rte_flow_desc_item[src->type].name) + /** + * allow PMD private flow item + */ + if (((int)src->type >= 0) && + ((size_t)src->type >= RTE_DIM(rte_flow_desc_item) || + !rte_flow_desc_item[src->type].name)) return rte_flow_error_set (error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, src, "cannot convert unknown item type"); @@ -752,8 +764,12 @@ rte_flow_conv_actions(struct rte_flow_action *dst, unsigned int i; for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) { - if ((size_t)src->type >= RTE_DIM(rte_flow_desc_action) || - !rte_flow_desc_action[src->type].name) + /** + * allow PMD private flow action + */ + if (((int)src->type >= 0) && + ((size_t)src->type >= RTE_DIM(rte_flow_desc_action) || + !rte_flow_desc_action[src->type].name)) return rte_flow_error_set (error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, src, "cannot convert unknown action type"); From patchwork Fri Oct 16 08:55:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gregory Etelson X-Patchwork-Id: 81066 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id C21C0A04DB; Fri, 16 Oct 2020 10:57:28 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 8FE591EBD1; Fri, 16 Oct 2020 10:56:42 +0200 (CEST) Received: from hqnvemgate25.nvidia.com (hqnvemgate25.nvidia.com [216.228.121.64]) by dpdk.org (Postfix) with ESMTP id B905F1EBD0 for ; Fri, 16 Oct 2020 10:56:39 +0200 (CEST) Received: from hqmail.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate25.nvidia.com (using TLS: TLSv1.2, AES256-SHA) id ; Fri, 16 Oct 2020 01:55:54 -0700 Received: from nvidia.com (10.124.1.5) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 16 Oct 2020 08:56:19 +0000 From: Gregory Etelson To: CC: , , , , , , , Eli Britstein , Ori Kam , Viacheslav Ovsiienko , "Ray Kinsella" , Neil Horman , "Thomas Monjalon" , Ferruh Yigit , Andrew Rybchenko Date: Fri, 16 Oct 2020 11:55:56 +0300 Message-ID: <20201016085557.18884-3-getelson@nvidia.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201016085557.18884-1-getelson@nvidia.com> References: <20200625160348.26220-1-getelson@mellanox.com> <20201016085557.18884-1-getelson@nvidia.com> MIME-Version: 1.0 X-Originating-IP: [10.124.1.5] X-ClientProxiedBy: HQMAIL111.nvidia.com (172.20.187.18) To HQMAIL107.nvidia.com (172.20.187.13) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1602838554; bh=oSXRYBwIhS9fp2114mmrtBud5Y14qrfgtTH3SH0OPjw=; h=From:To:CC:Subject:Date:Message-ID:X-Mailer:In-Reply-To: References:MIME-Version:Content-Transfer-Encoding:Content-Type: X-Originating-IP:X-ClientProxiedBy; b=AF177II47AKWiC2ehz+jeJsm+oCI5J0EAxLFimtDxPdmp7zShaBROgC9KMLtURS3a wKQRfpAv32efMnaNWK87XroveplUQfb9ZzkmKyrmu0DpbrfehjpRRsy1nDpe+3Cm7W WptABgtcNj/CIsOqBtqA39QFoej1VZA1uz2XCFqrTmgNhC/P58qZ4Txn/8UXhEFK+S l7nZ3Iogd6bcz23+ij4+gALQ8cFUA+1AI9UXRIpusOdsk2WoBYMoolOrAeANZMmw94 dmWgvhoUyefzxbZZz+38Nreggor3Xw7rkOI5MiMiWRLS4Wy8lPcP15+tFHQ8z1Ulmp 9x7u3SNRbqswg== Subject: [dpdk-dev] [PATCH v6 2/3] ethdev: tunnel offload model X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Eli Britstein rte_flow API provides the building blocks for vendor-agnostic flow classification offloads. The rte_flow "patterns" and "actions" primitives are fine-grained, thus enabling DPDK applications the flexibility to offload network stacks and complex pipelines. Applications wishing to offload tunneled traffic are required to use the rte_flow primitives, such as group, meta, mark, tag, and others to model their high-level objects. The hardware model design for high-level software objects is not trivial. Furthermore, an optimal design is often vendor-specific. When hardware offloads tunneled traffic in multi-group logic, partially offloaded packets may arrive to the application after they were modified in hardware. In this case, the application may need to restore the original packet headers. Consider the following sequence: The application decaps a packet in one group and jumps to a second group where it tries to match on a 5-tuple, that will miss and send the packet to the application. In this case, the application does not receive the original packet but a modified one. Also, in this case, the application cannot match on the outer header fields, such as VXLAN vni and 5-tuple. There are several possible ways to use rte_flow "patterns" and "actions" to resolve the issues above. For example: 1 Mapping headers to a hardware registers using the rte_flow_action_mark/rte_flow_action_tag/rte_flow_set_meta objects. 2 Apply the decap only at the last offload stage after all the "patterns" were matched and the packet will be fully offloaded. Every approach has its pros and cons and is highly dependent on the hardware vendor. For example, some hardware may have a limited number of registers while other hardware could not support inner actions and must decap before accessing inner headers. The tunnel offload model resolves these issues. The model goals are: 1 Provide a unified application API to offload tunneled traffic that is capable to match on outer headers after decap. 2 Allow the application to restore the outer header of partially offloaded packets. The tunnel offload model does not introduce new elements to the existing RTE flow model and is implemented as a set of helper functions. For the application to work with the tunnel offload API it has to adjust flow rules in multi-table tunnel offload in the following way: 1 Remove explicit call to decap action and replace it with PMD actions obtained from rte_flow_tunnel_decap_and_set() helper. 2 Add PMD items obtained from rte_flow_tunnel_match() helper to all other rules in the tunnel offload sequence. VXLAN Code example: Assume application needs to do inner NAT on the VXLAN packet. The first rule in group 0: flow create ingress group 0 pattern eth / ipv4 / udp dst is 4789 / vxlan / end actions {pmd actions} / jump group 3 / end The first VXLAN packet that arrives matches the rule in group 0 and jumps to group 3. In group 3 the packet will miss since there is no flow to match and will be sent to the application. Application will call rte_flow_get_restore_info() to get the packet outer header. Application will insert a new rule in group 3 to match outer and inner headers: flow create ingress group 3 pattern {pmd items} / eth / ipv4 dst is 172.10.10.1 / udp dst 4789 / vxlan vni is 10 / ipv4 dst is 184.1.2.3 / end actions set_ipv4_dst 186.1.1.1 / queue index 3 / end Resulting of the rules will be that VXLAN packet with vni=10, outer IPv4 dst=172.10.10.1 and inner IPv4 dst=184.1.2.3 will be received decapped on queue 3 with IPv4 dst=186.1.1.1 Note: The packet in group 3 is considered decapped. All actions in that group will be done on the header that was inner before decap. The application may specify an outer header to be matched on. It's PMD responsibility to translate these items to outer metadata. API usage: /** * 1. Initiate RTE flow tunnel object */ const struct rte_flow_tunnel tunnel = { .type = RTE_FLOW_ITEM_TYPE_VXLAN, .tun_id = 10, } /** * 2. Obtain PMD tunnel actions * * pmd_actions is an intermediate variable application uses to * compile actions array */ struct rte_flow_action **pmd_actions; rte_flow_tunnel_decap_and_set(&tunnel, &pmd_actions, &num_pmd_actions, &error); /** * 3. offload the first rule * matching on VXLAN traffic and jumps to group 3 * (implicitly decaps packet) */ app_actions = jump group 3 rule_items = app_items; /** eth / ipv4 / udp / vxlan */ rule_actions = { pmd_actions, app_actions }; attr.group = 0; flow_1 = rte_flow_create(port_id, &attr, rule_items, rule_actions, &error); /** * 4. after flow creation application does not need to keep the * tunnel action resources. */ rte_flow_tunnel_action_release(port_id, pmd_actions, num_pmd_actions); /** * 5. After partially offloaded packet miss because there was no * matching rule handle miss on group 3 */ struct rte_flow_restore_info info; rte_flow_get_restore_info(port_id, mbuf, &info, &error); /** * 6. Offload NAT rule: */ app_items = { eth / ipv4 dst is 172.10.10.1 / udp dst 4789 / vxlan vni is 10 / ipv4 dst is 184.1.2.3 } app_actions = { set_ipv4_dst 186.1.1.1 / queue index 3 } rte_flow_tunnel_match(&info.tunnel, &pmd_items, &num_pmd_items, &error); rule_items = {pmd_items, app_items}; rule_actions = app_actions; attr.group = info.group_id; flow_2 = rte_flow_create(port_id, &attr, rule_items, rule_actions, &error); /** * 7. Release PMD items after rule creation */ rte_flow_tunnel_item_release(port_id, pmd_items, num_pmd_items); References 1. https://mails.dpdk.org/archives/dev/2020-June/index.html Signed-off-by: Eli Britstein Signed-off-by: Gregory Etelson Acked-by: Ori Kam Acked-by: Viacheslav Ovsiienko --- v5: * rebase to next-net v6: * update the patch comment * update tunnel offload section in rte_flow.rst --- doc/guides/prog_guide/rte_flow.rst | 78 +++++++++ doc/guides/rel_notes/release_20_11.rst | 5 + lib/librte_ethdev/rte_ethdev_version.map | 5 + lib/librte_ethdev/rte_flow.c | 112 +++++++++++++ lib/librte_ethdev/rte_flow.h | 195 +++++++++++++++++++++++ lib/librte_ethdev/rte_flow_driver.h | 32 ++++ 6 files changed, 427 insertions(+) diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst index 7fb5ec9059..8dc048c6f4 100644 --- a/doc/guides/prog_guide/rte_flow.rst +++ b/doc/guides/prog_guide/rte_flow.rst @@ -3131,6 +3131,84 @@ operations include: - Duplication of a complete flow rule description. - Pattern item or action name retrieval. +Tunneled traffic offload +~~~~~~~~~~~~~~~~~~~~~~~~ + +rte_flow API provides the building blocks for vendor-agnostic flow +classification offloads. The rte_flow "patterns" and "actions" +primitives are fine-grained, thus enabling DPDK applications the +flexibility to offload network stacks and complex pipelines. +Applications wishing to offload tunneled traffic are required to use +the rte_flow primitives, such as group, meta, mark, tag, and others to +model their high-level objects. The hardware model design for +high-level software objects is not trivial. Furthermore, an optimal +design is often vendor-specific. + +When hardware offloads tunneled traffic in multi-group logic, +partially offloaded packets may arrive to the application after they +were modified in hardware. In this case, the application may need to +restore the original packet headers. Consider the following sequence: +The application decaps a packet in one group and jumps to a second +group where it tries to match on a 5-tuple, that will miss and send +the packet to the application. In this case, the application does not +receive the original packet but a modified one. Also, in this case, +the application cannot match on the outer header fields, such as VXLAN +vni and 5-tuple. + +There are several possible ways to use rte_flow "patterns" and +"actions" to resolve the issues above. For example: + +1 Mapping headers to a hardware registers using the +rte_flow_action_mark/rte_flow_action_tag/rte_flow_set_meta objects. + +2 Apply the decap only at the last offload stage after all the +"patterns" were matched and the packet will be fully offloaded. + +Every approach has its pros and cons and is highly dependent on the +hardware vendor. For example, some hardware may have a limited number +of registers while other hardware could not support inner actions and +must decap before accessing inner headers. + +The tunnel offload model resolves these issues. The model goals are: + +1 Provide a unified application API to offload tunneled traffic that +is capable to match on outer headers after decap. + +2 Allow the application to restore the outer header of partially +offloaded packets. + +The tunnel offload model does not introduce new elements to the +existing RTE flow model and is implemented as a set of helper +functions. + +For the application to work with the tunnel offload API it +has to adjust flow rules in multi-table tunnel offload in the +following way: + +1 Remove explicit call to decap action and replace it with PMD actions +obtained from rte_flow_tunnel_decap_and_set() helper. + +2 Add PMD items obtained from rte_flow_tunnel_match() helper to all +other rules in the tunnel offload sequence. + +The model requirements: + +Software application must initialize +rte_tunnel object with tunnel parameters before calling +rte_flow_tunnel_decap_set() & rte_flow_tunnel_match(). + +PMD actions array obtained in rte_flow_tunnel_decap_set() must be +released by application with rte_flow_action_release() call. + +PMD items array obtained with rte_flow_tunnel_match() must be released +by application with rte_flow_item_release() call. Application can +release PMD items and actions after rule was created. However, if the +application needs to create additional rule for the same tunnel it +will need to obtain PMD items again. + +Application cannot destroy rte_tunnel object before it releases all +PMD actions & PMD items referencing that tunnel. + Caveats ------- diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst index 9155b468d6..f125ce79dd 100644 --- a/doc/guides/rel_notes/release_20_11.rst +++ b/doc/guides/rel_notes/release_20_11.rst @@ -121,6 +121,11 @@ New Features * Flow rule verification was updated to accept private PMD items and actions. +* **Added generic API to offload tunneled traffic and restore missed packet.** + + * Added a new hardware independent helper API to RTE flow library that + offloads tunneled traffic and restores missed packets. + * **Updated Cisco enic driver.** * Added support for VF representors with single-queue Tx/Rx and flow API diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map index f64c379ac2..8ddda2547f 100644 --- a/lib/librte_ethdev/rte_ethdev_version.map +++ b/lib/librte_ethdev/rte_ethdev_version.map @@ -239,6 +239,11 @@ EXPERIMENTAL { rte_flow_shared_action_destroy; rte_flow_shared_action_query; rte_flow_shared_action_update; + rte_flow_tunnel_decap_set; + rte_flow_tunnel_match; + rte_flow_get_restore_info; + rte_flow_tunnel_action_decap_release; + rte_flow_tunnel_item_release; }; INTERNAL { diff --git a/lib/librte_ethdev/rte_flow.c b/lib/librte_ethdev/rte_flow.c index b74ea5593a..380c5cae2c 100644 --- a/lib/librte_ethdev/rte_flow.c +++ b/lib/librte_ethdev/rte_flow.c @@ -1143,3 +1143,115 @@ rte_flow_shared_action_query(uint16_t port_id, data, error); return flow_err(port_id, ret, error); } + +int +rte_flow_tunnel_decap_set(uint16_t port_id, + struct rte_flow_tunnel *tunnel, + struct rte_flow_action **actions, + uint32_t *num_of_actions, + struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error); + + if (unlikely(!ops)) + return -rte_errno; + if (likely(!!ops->tunnel_decap_set)) { + return flow_err(port_id, + ops->tunnel_decap_set(dev, tunnel, actions, + num_of_actions, error), + error); + } + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, rte_strerror(ENOTSUP)); +} + +int +rte_flow_tunnel_match(uint16_t port_id, + struct rte_flow_tunnel *tunnel, + struct rte_flow_item **items, + uint32_t *num_of_items, + struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error); + + if (unlikely(!ops)) + return -rte_errno; + if (likely(!!ops->tunnel_match)) { + return flow_err(port_id, + ops->tunnel_match(dev, tunnel, items, + num_of_items, error), + error); + } + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, rte_strerror(ENOTSUP)); +} + +int +rte_flow_get_restore_info(uint16_t port_id, + struct rte_mbuf *m, + struct rte_flow_restore_info *restore_info, + struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error); + + if (unlikely(!ops)) + return -rte_errno; + if (likely(!!ops->get_restore_info)) { + return flow_err(port_id, + ops->get_restore_info(dev, m, restore_info, + error), + error); + } + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, rte_strerror(ENOTSUP)); +} + +int +rte_flow_tunnel_action_decap_release(uint16_t port_id, + struct rte_flow_action *actions, + uint32_t num_of_actions, + struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error); + + if (unlikely(!ops)) + return -rte_errno; + if (likely(!!ops->action_release)) { + return flow_err(port_id, + ops->action_release(dev, actions, + num_of_actions, error), + error); + } + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, rte_strerror(ENOTSUP)); +} + +int +rte_flow_tunnel_item_release(uint16_t port_id, + struct rte_flow_item *items, + uint32_t num_of_items, + struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error); + + if (unlikely(!ops)) + return -rte_errno; + if (likely(!!ops->item_release)) { + return flow_err(port_id, + ops->item_release(dev, items, + num_of_items, error), + error); + } + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, rte_strerror(ENOTSUP)); +} diff --git a/lib/librte_ethdev/rte_flow.h b/lib/librte_ethdev/rte_flow.h index 48395284b5..a8eac4deb8 100644 --- a/lib/librte_ethdev/rte_flow.h +++ b/lib/librte_ethdev/rte_flow.h @@ -3620,6 +3620,201 @@ rte_flow_shared_action_query(uint16_t port_id, void *data, struct rte_flow_error *error); +/* Tunnel has a type and the key information. */ +struct rte_flow_tunnel { + /** + * Tunnel type, for example RTE_FLOW_ITEM_TYPE_VXLAN, + * RTE_FLOW_ITEM_TYPE_NVGRE etc. + */ + enum rte_flow_item_type type; + uint64_t tun_id; /**< Tunnel identification. */ + + RTE_STD_C11 + union { + struct { + rte_be32_t src_addr; /**< IPv4 source address. */ + rte_be32_t dst_addr; /**< IPv4 destination address. */ + } ipv4; + struct { + uint8_t src_addr[16]; /**< IPv6 source address. */ + uint8_t dst_addr[16]; /**< IPv6 destination address. */ + } ipv6; + }; + rte_be16_t tp_src; /**< Tunnel port source. */ + rte_be16_t tp_dst; /**< Tunnel port destination. */ + uint16_t tun_flags; /**< Tunnel flags. */ + + bool is_ipv6; /**< True for valid IPv6 fields. Otherwise IPv4. */ + + /** + * the following members are required to restore packet + * after miss + */ + uint8_t tos; /**< TOS for IPv4, TC for IPv6. */ + uint8_t ttl; /**< TTL for IPv4, HL for IPv6. */ + uint32_t label; /**< Flow Label for IPv6. */ +}; + +/** + * Indicate that the packet has a tunnel. + */ +#define RTE_FLOW_RESTORE_INFO_TUNNEL (1ULL << 0) + +/** + * Indicate that the packet has a non decapsulated tunnel header. + */ +#define RTE_FLOW_RESTORE_INFO_ENCAPSULATED (1ULL << 1) + +/** + * Indicate that the packet has a group_id. + */ +#define RTE_FLOW_RESTORE_INFO_GROUP_ID (1ULL << 2) + +/** + * Restore information structure to communicate the current packet processing + * state when some of the processing pipeline is done in hardware and should + * continue in software. + */ +struct rte_flow_restore_info { + /** + * Bitwise flags (RTE_FLOW_RESTORE_INFO_*) to indicate validation of + * other fields in struct rte_flow_restore_info. + */ + uint64_t flags; + uint32_t group_id; /**< Group ID where packed missed */ + struct rte_flow_tunnel tunnel; /**< Tunnel information. */ +}; + +/** + * Allocate an array of actions to be used in rte_flow_create, to implement + * tunnel-decap-set for the given tunnel. + * Sample usage: + * actions vxlan_decap / tunnel-decap-set(tunnel properties) / + * jump group 0 / end + * + * @param port_id + * Port identifier of Ethernet device. + * @param[in] tunnel + * Tunnel properties. + * @param[out] actions + * Array of actions to be allocated by the PMD. This array should be + * concatenated with the actions array provided to rte_flow_create. + * @param[out] num_of_actions + * Number of actions allocated. + * @param[out] error + * Perform verbose error reporting if not NULL. PMDs initialize this + * structure in case of error only. + * + * @return + * 0 on success, a negative errno value otherwise and rte_errno is set. + */ +__rte_experimental +int +rte_flow_tunnel_decap_set(uint16_t port_id, + struct rte_flow_tunnel *tunnel, + struct rte_flow_action **actions, + uint32_t *num_of_actions, + struct rte_flow_error *error); + +/** + * Allocate an array of items to be used in rte_flow_create, to implement + * tunnel-match for the given tunnel. + * Sample usage: + * pattern tunnel-match(tunnel properties) / outer-header-matches / + * inner-header-matches / end + * + * @param port_id + * Port identifier of Ethernet device. + * @param[in] tunnel + * Tunnel properties. + * @param[out] items + * Array of items to be allocated by the PMD. This array should be + * concatenated with the items array provided to rte_flow_create. + * @param[out] num_of_items + * Number of items allocated. + * @param[out] error + * Perform verbose error reporting if not NULL. PMDs initialize this + * structure in case of error only. + * + * @return + * 0 on success, a negative errno value otherwise and rte_errno is set. + */ +__rte_experimental +int +rte_flow_tunnel_match(uint16_t port_id, + struct rte_flow_tunnel *tunnel, + struct rte_flow_item **items, + uint32_t *num_of_items, + struct rte_flow_error *error); + +/** + * Populate the current packet processing state, if exists, for the given mbuf. + * + * @param port_id + * Port identifier of Ethernet device. + * @param[in] m + * Mbuf struct. + * @param[out] info + * Restore information. Upon success contains the HW state. + * @param[out] error + * Perform verbose error reporting if not NULL. PMDs initialize this + * structure in case of error only. + * + * @return + * 0 on success, a negative errno value otherwise and rte_errno is set. + */ +__rte_experimental +int +rte_flow_get_restore_info(uint16_t port_id, + struct rte_mbuf *m, + struct rte_flow_restore_info *info, + struct rte_flow_error *error); + +/** + * Release the action array as allocated by rte_flow_tunnel_decap_set. + * + * @param port_id + * Port identifier of Ethernet device. + * @param[in] actions + * Array of actions to be released. + * @param[in] num_of_actions + * Number of elements in actions array. + * @param[out] error + * Perform verbose error reporting if not NULL. PMDs initialize this + * structure in case of error only. + * + * @return + * 0 on success, a negative errno value otherwise and rte_errno is set. + */ +__rte_experimental +int +rte_flow_tunnel_action_decap_release(uint16_t port_id, + struct rte_flow_action *actions, + uint32_t num_of_actions, + struct rte_flow_error *error); + +/** + * Release the item array as allocated by rte_flow_tunnel_match. + * + * @param port_id + * Port identifier of Ethernet device. + * @param[in] items + * Array of items to be released. + * @param[in] num_of_items + * Number of elements in item array. + * @param[out] error + * Perform verbose error reporting if not NULL. PMDs initialize this + * structure in case of error only. + * + * @return + * 0 on success, a negative errno value otherwise and rte_errno is set. + */ +__rte_experimental +int +rte_flow_tunnel_item_release(uint16_t port_id, + struct rte_flow_item *items, + uint32_t num_of_items, + struct rte_flow_error *error); #ifdef __cplusplus } #endif diff --git a/lib/librte_ethdev/rte_flow_driver.h b/lib/librte_ethdev/rte_flow_driver.h index 58f56b0262..bd5ffc0bb1 100644 --- a/lib/librte_ethdev/rte_flow_driver.h +++ b/lib/librte_ethdev/rte_flow_driver.h @@ -131,6 +131,38 @@ struct rte_flow_ops { const struct rte_flow_shared_action *shared_action, void *data, struct rte_flow_error *error); + /** See rte_flow_tunnel_decap_set() */ + int (*tunnel_decap_set) + (struct rte_eth_dev *dev, + struct rte_flow_tunnel *tunnel, + struct rte_flow_action **pmd_actions, + uint32_t *num_of_actions, + struct rte_flow_error *err); + /** See rte_flow_tunnel_match() */ + int (*tunnel_match) + (struct rte_eth_dev *dev, + struct rte_flow_tunnel *tunnel, + struct rte_flow_item **pmd_items, + uint32_t *num_of_items, + struct rte_flow_error *err); + /** See rte_flow_get_rte_flow_restore_info() */ + int (*get_restore_info) + (struct rte_eth_dev *dev, + struct rte_mbuf *m, + struct rte_flow_restore_info *info, + struct rte_flow_error *err); + /** See rte_flow_action_tunnel_decap_release() */ + int (*action_release) + (struct rte_eth_dev *dev, + struct rte_flow_action *pmd_actions, + uint32_t num_of_actions, + struct rte_flow_error *err); + /** See rte_flow_item_release() */ + int (*item_release) + (struct rte_eth_dev *dev, + struct rte_flow_item *pmd_items, + uint32_t num_of_items, + struct rte_flow_error *err); }; /** From patchwork Fri Oct 16 08:55:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gregory Etelson X-Patchwork-Id: 81065 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 5DC53A04DB; Fri, 16 Oct 2020 10:56:59 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 7D7881EBC5; Fri, 16 Oct 2020 10:56:38 +0200 (CEST) Received: from hqnvemgate25.nvidia.com (hqnvemgate25.nvidia.com [216.228.121.64]) by dpdk.org (Postfix) with ESMTP id BD0A41EBBA for ; Fri, 16 Oct 2020 10:56:35 +0200 (CEST) Received: from hqmail.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate25.nvidia.com (using TLS: TLSv1.2, AES256-SHA) id ; Fri, 16 Oct 2020 01:55:49 -0700 Received: from nvidia.com (10.124.1.5) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Fri, 16 Oct 2020 08:56:23 +0000 From: Gregory Etelson To: CC: , , , , , , , Ori Kam , Wenzhuo Lu , Beilei Xing , "Bernard Iremonger" Date: Fri, 16 Oct 2020 11:55:57 +0300 Message-ID: <20201016085557.18884-4-getelson@nvidia.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201016085557.18884-1-getelson@nvidia.com> References: <20200625160348.26220-1-getelson@mellanox.com> <20201016085557.18884-1-getelson@nvidia.com> MIME-Version: 1.0 X-Originating-IP: [10.124.1.5] X-ClientProxiedBy: HQMAIL111.nvidia.com (172.20.187.18) To HQMAIL107.nvidia.com (172.20.187.13) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1602838549; bh=RD7haO+bhKJ/YAd+idUQdHvD9lnYxRhn+3qtgcv+iG8=; h=From:To:CC:Subject:Date:Message-ID:X-Mailer:In-Reply-To: References:MIME-Version:Content-Transfer-Encoding:Content-Type: X-Originating-IP:X-ClientProxiedBy; b=SFxXBHhBg09YSSEckfgVuuGjBP/wlxsmnETqaFwPMB5R9VPjDGyGFhwE/1HRDRPoq y0XgKMPewZr+kX74W4eoZG3sHdW+H3x+5vj+OV8wBBCDUQKARIU+Kc0EedfSlc/qgW YrH8iVDGJL90ifnJFN1Hex4cjMKQoRW2VuxsLk2oY4lgEtfjk9LO50tgqN7JvqrAyQ uNSGNMYHzBe5QXP45CuFopZ82OZGDIZvo42DZmp/HrZH/NymxX0BnIeflQWGhorlf9 xqeb7XZhmd8ckw3iWZvKb21SBnEL5U50pD3oWDkIZCYdLMypYLEIOoIEfT/h9WyieR t8RRg5C/fxYkQ== Subject: [dpdk-dev] [PATCH v6 3/3] app/testpmd: add commands for tunnel offload API X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Tunnel Offload API provides hardware independent, unified model to offload tunneled traffic. Key model elements are: - apply matches to both outer and inner packet headers during entire offload procedure; - restore outer header of partially offloaded packet; - model is implemented as a set of helper functions. Implementation details: * Create application tunnel: flow tunnel create type On success, the command creates application tunnel object and returns the tunnel descriptor. Tunnel descriptor is used in subsequent flow creation commands to reference the tunnel. * Create tunnel steering flow rule: tunnel_set parameter used with steering rule template. * Create tunnel matching flow rule: tunnel_match used with matching rule template. * If tunnel steering rule was offloaded, outer header of a partially offloaded packet is restored after miss. Example: test packet= >>>>>> >>> len(packet) 92 testpmd> flow flush 0 testpmd> port 0/queue 0: received 1 packets src=50:6B:4B:CC:FC:E2 - dst=24:8A:07:8D:AE:D6 - type=0x0800 - length=92 testpmd> flow tunnel 0 type vxlan port 0: flow tunnel #1 type vxlan testpmd> flow create 0 ingress group 0 tunnel_set 1 pattern eth /ipv4 / udp dst is 4789 / vxlan / end actions jump group 0 / end Flow rule #0 created testpmd> port 0/queue 0: received 1 packets tunnel restore info: - vxlan tunnel - outer header present # <-- src=50:6B:4B:CC:FC:E2 - dst=24:8A:07:8D:AE:D6 - type=0x0800 - length=92 testpmd> flow create 0 ingress group 0 tunnel_match 1 pattern eth / ipv4 / udp dst is 4789 / vxlan / eth / ipv4 / end actions set_mac_dst mac_addr 02:CA:FE:CA:FA:80 / queue index 0 / end Flow rule #1 created testpmd> port 0/queue 0: received 1 packets src=50:BB:BB:BB:BB:E2 - dst=02:CA:FE:CA:FA:80 - type=0x0800 - length=42 * Destroy flow tunnel flow tunnel destroy id * Show existing flow tunnels flow tunnel list Signed-off-by: Gregory Etelson --- v2: * introduce testpmd support for tunnel offload API v3: * update flow tunnel commands v5: * rebase to next-net --- app/test-pmd/cmdline_flow.c | 170 ++++++++++++- app/test-pmd/config.c | 252 +++++++++++++++++++- app/test-pmd/testpmd.c | 5 +- app/test-pmd/testpmd.h | 34 ++- app/test-pmd/util.c | 35 ++- doc/guides/testpmd_app_ug/testpmd_funcs.rst | 49 ++++ 6 files changed, 532 insertions(+), 13 deletions(-) diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c index 00c70a144a..b9a1f7178a 100644 --- a/app/test-pmd/cmdline_flow.c +++ b/app/test-pmd/cmdline_flow.c @@ -74,6 +74,14 @@ enum index { LIST, AGED, ISOLATE, + TUNNEL, + + /* Tunnel arguments. */ + TUNNEL_CREATE, + TUNNEL_CREATE_TYPE, + TUNNEL_LIST, + TUNNEL_DESTROY, + TUNNEL_DESTROY_ID, /* Destroy arguments. */ DESTROY_RULE, @@ -93,6 +101,8 @@ enum index { INGRESS, EGRESS, TRANSFER, + TUNNEL_SET, + TUNNEL_MATCH, /* Shared action arguments */ SHARED_ACTION_CREATE, @@ -713,6 +723,7 @@ struct buffer { } sa; /* Shared action query arguments */ struct { struct rte_flow_attr attr; + struct tunnel_ops tunnel_ops; struct rte_flow_item *pattern; struct rte_flow_action *actions; uint32_t pattern_n; @@ -789,10 +800,32 @@ static const enum index next_vc_attr[] = { INGRESS, EGRESS, TRANSFER, + TUNNEL_SET, + TUNNEL_MATCH, PATTERN, ZERO, }; +static const enum index tunnel_create_attr[] = { + TUNNEL_CREATE, + TUNNEL_CREATE_TYPE, + END, + ZERO, +}; + +static const enum index tunnel_destroy_attr[] = { + TUNNEL_DESTROY, + TUNNEL_DESTROY_ID, + END, + ZERO, +}; + +static const enum index tunnel_list_attr[] = { + TUNNEL_LIST, + END, + ZERO, +}; + static const enum index next_destroy_attr[] = { DESTROY_RULE, END, @@ -1643,6 +1676,9 @@ static int parse_aged(struct context *, const struct token *, static int parse_isolate(struct context *, const struct token *, const char *, unsigned int, void *, unsigned int); +static int parse_tunnel(struct context *, const struct token *, + const char *, unsigned int, + void *, unsigned int); static int parse_int(struct context *, const struct token *, const char *, unsigned int, void *, unsigned int); @@ -1844,7 +1880,8 @@ static const struct token token_list[] = { LIST, AGED, QUERY, - ISOLATE)), + ISOLATE, + TUNNEL)), .call = parse_init, }, /* Top-level command. */ @@ -1955,6 +1992,49 @@ static const struct token token_list[] = { ARGS_ENTRY(struct buffer, port)), .call = parse_isolate, }, + [TUNNEL] = { + .name = "tunnel", + .help = "new tunnel API", + .next = NEXT(NEXT_ENTRY + (TUNNEL_CREATE, TUNNEL_LIST, TUNNEL_DESTROY)), + .call = parse_tunnel, + }, + /* Tunnel arguments. */ + [TUNNEL_CREATE] = { + .name = "create", + .help = "create new tunnel object", + .next = NEXT(tunnel_create_attr, NEXT_ENTRY(PORT_ID)), + .args = ARGS(ARGS_ENTRY(struct buffer, port)), + .call = parse_tunnel, + }, + [TUNNEL_CREATE_TYPE] = { + .name = "type", + .help = "create new tunnel", + .next = NEXT(tunnel_create_attr, NEXT_ENTRY(FILE_PATH)), + .args = ARGS(ARGS_ENTRY(struct tunnel_ops, type)), + .call = parse_tunnel, + }, + [TUNNEL_DESTROY] = { + .name = "destroy", + .help = "destroy tunel", + .next = NEXT(tunnel_destroy_attr, NEXT_ENTRY(PORT_ID)), + .args = ARGS(ARGS_ENTRY(struct buffer, port)), + .call = parse_tunnel, + }, + [TUNNEL_DESTROY_ID] = { + .name = "id", + .help = "tunnel identifier to testroy", + .next = NEXT(tunnel_destroy_attr, NEXT_ENTRY(UNSIGNED)), + .args = ARGS(ARGS_ENTRY(struct tunnel_ops, id)), + .call = parse_tunnel, + }, + [TUNNEL_LIST] = { + .name = "list", + .help = "list existing tunnels", + .next = NEXT(tunnel_list_attr, NEXT_ENTRY(PORT_ID)), + .args = ARGS(ARGS_ENTRY(struct buffer, port)), + .call = parse_tunnel, + }, /* Destroy arguments. */ [DESTROY_RULE] = { .name = "rule", @@ -2018,6 +2098,20 @@ static const struct token token_list[] = { .next = NEXT(next_vc_attr), .call = parse_vc, }, + [TUNNEL_SET] = { + .name = "tunnel_set", + .help = "tunnel steer rule", + .next = NEXT(next_vc_attr, NEXT_ENTRY(UNSIGNED)), + .args = ARGS(ARGS_ENTRY(struct tunnel_ops, id)), + .call = parse_vc, + }, + [TUNNEL_MATCH] = { + .name = "tunnel_match", + .help = "tunnel match rule", + .next = NEXT(next_vc_attr, NEXT_ENTRY(UNSIGNED)), + .args = ARGS(ARGS_ENTRY(struct tunnel_ops, id)), + .call = parse_vc, + }, /* Validate/create pattern. */ [PATTERN] = { .name = "pattern", @@ -4495,12 +4589,28 @@ parse_vc(struct context *ctx, const struct token *token, return len; } ctx->objdata = 0; - ctx->object = &out->args.vc.attr; + switch (ctx->curr) { + default: + ctx->object = &out->args.vc.attr; + break; + case TUNNEL_SET: + case TUNNEL_MATCH: + ctx->object = &out->args.vc.tunnel_ops; + break; + } ctx->objmask = NULL; switch (ctx->curr) { case GROUP: case PRIORITY: return len; + case TUNNEL_SET: + out->args.vc.tunnel_ops.enabled = 1; + out->args.vc.tunnel_ops.actions = 1; + return len; + case TUNNEL_MATCH: + out->args.vc.tunnel_ops.enabled = 1; + out->args.vc.tunnel_ops.items = 1; + return len; case INGRESS: out->args.vc.attr.ingress = 1; return len; @@ -6108,6 +6218,47 @@ parse_isolate(struct context *ctx, const struct token *token, return len; } +static int +parse_tunnel(struct context *ctx, const struct token *token, + const char *str, unsigned int len, + void *buf, unsigned int size) +{ + struct buffer *out = buf; + + /* Token name must match. */ + if (parse_default(ctx, token, str, len, NULL, 0) < 0) + return -1; + /* Nothing else to do if there is no buffer. */ + if (!out) + return len; + if (!out->command) { + if (ctx->curr != TUNNEL) + return -1; + if (sizeof(*out) > size) + return -1; + out->command = ctx->curr; + ctx->objdata = 0; + ctx->object = out; + ctx->objmask = NULL; + } else { + switch (ctx->curr) { + default: + break; + case TUNNEL_CREATE: + case TUNNEL_DESTROY: + case TUNNEL_LIST: + out->command = ctx->curr; + break; + case TUNNEL_CREATE_TYPE: + case TUNNEL_DESTROY_ID: + ctx->object = &out->args.vc.tunnel_ops; + break; + } + } + + return len; +} + /** * Parse signed/unsigned integers 8 to 64-bit long. * @@ -7148,11 +7299,13 @@ cmd_flow_parsed(const struct buffer *in) break; case VALIDATE: port_flow_validate(in->port, &in->args.vc.attr, - in->args.vc.pattern, in->args.vc.actions); + in->args.vc.pattern, in->args.vc.actions, + &in->args.vc.tunnel_ops); break; case CREATE: port_flow_create(in->port, &in->args.vc.attr, - in->args.vc.pattern, in->args.vc.actions); + in->args.vc.pattern, in->args.vc.actions, + &in->args.vc.tunnel_ops); break; case DESTROY: port_flow_destroy(in->port, in->args.destroy.rule_n, @@ -7178,6 +7331,15 @@ cmd_flow_parsed(const struct buffer *in) case AGED: port_flow_aged(in->port, in->args.aged.destroy); break; + case TUNNEL_CREATE: + port_flow_tunnel_create(in->port, &in->args.vc.tunnel_ops); + break; + case TUNNEL_DESTROY: + port_flow_tunnel_destroy(in->port, in->args.vc.tunnel_ops.id); + break; + case TUNNEL_LIST: + port_flow_tunnel_list(in->port); + break; default: break; } diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 2c00b55440..6bce791dfb 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -1521,6 +1521,115 @@ port_mtu_set(portid_t port_id, uint16_t mtu) /* Generic flow management functions. */ +static struct port_flow_tunnel * +port_flow_locate_tunnel_id(struct rte_port *port, uint32_t port_tunnel_id) +{ + struct port_flow_tunnel *flow_tunnel; + + LIST_FOREACH(flow_tunnel, &port->flow_tunnel_list, chain) { + if (flow_tunnel->id == port_tunnel_id) + goto out; + } + flow_tunnel = NULL; + +out: + return flow_tunnel; +} + +const char * +port_flow_tunnel_type(struct rte_flow_tunnel *tunnel) +{ + const char *type; + switch (tunnel->type) { + default: + type = "unknown"; + break; + case RTE_FLOW_ITEM_TYPE_VXLAN: + type = "vxlan"; + break; + } + + return type; +} + +struct port_flow_tunnel * +port_flow_locate_tunnel(uint16_t port_id, struct rte_flow_tunnel *tun) +{ + struct rte_port *port = &ports[port_id]; + struct port_flow_tunnel *flow_tunnel; + + LIST_FOREACH(flow_tunnel, &port->flow_tunnel_list, chain) { + if (!memcmp(&flow_tunnel->tunnel, tun, sizeof(*tun))) + goto out; + } + flow_tunnel = NULL; + +out: + return flow_tunnel; +} + +void port_flow_tunnel_list(portid_t port_id) +{ + struct rte_port *port = &ports[port_id]; + struct port_flow_tunnel *flt; + + LIST_FOREACH(flt, &port->flow_tunnel_list, chain) { + printf("port %u tunnel #%u type=%s", + port_id, flt->id, port_flow_tunnel_type(&flt->tunnel)); + if (flt->tunnel.tun_id) + printf(" id=%lu", flt->tunnel.tun_id); + printf("\n"); + } +} + +void port_flow_tunnel_destroy(portid_t port_id, uint32_t tunnel_id) +{ + struct rte_port *port = &ports[port_id]; + struct port_flow_tunnel *flt; + + LIST_FOREACH(flt, &port->flow_tunnel_list, chain) { + if (flt->id == tunnel_id) + break; + } + if (flt) { + LIST_REMOVE(flt, chain); + free(flt); + printf("port %u: flow tunnel #%u destroyed\n", + port_id, tunnel_id); + } +} + +void port_flow_tunnel_create(portid_t port_id, const struct tunnel_ops *ops) +{ + struct rte_port *port = &ports[port_id]; + enum rte_flow_item_type type; + struct port_flow_tunnel *flt; + + if (!strcmp(ops->type, "vxlan")) + type = RTE_FLOW_ITEM_TYPE_VXLAN; + else { + printf("cannot offload \"%s\" tunnel type\n", ops->type); + return; + } + LIST_FOREACH(flt, &port->flow_tunnel_list, chain) { + if (flt->tunnel.type == type) + break; + } + if (!flt) { + flt = calloc(1, sizeof(*flt)); + if (!flt) { + printf("failed to allocate port flt object\n"); + return; + } + flt->tunnel.type = type; + flt->id = LIST_EMPTY(&port->flow_tunnel_list) ? 1 : + LIST_FIRST(&port->flow_tunnel_list)->id + 1; + LIST_INSERT_HEAD(&port->flow_tunnel_list, flt, chain); + } + printf("port %d: flow tunnel #%u type %s\n", + port_id, flt->id, ops->type); +} + /** Generate a port_flow entry from attributes/pattern/actions. */ static struct port_flow * port_flow_new(const struct rte_flow_attr *attr, @@ -1860,20 +1969,137 @@ port_shared_action_query(portid_t port_id, uint32_t id) } return ret; } +static struct port_flow_tunnel * +port_flow_tunnel_offload_cmd_prep(portid_t port_id, + const struct rte_flow_item *pattern, + const struct rte_flow_action *actions, + const struct tunnel_ops *tunnel_ops) +{ + int ret; + struct rte_port *port; + struct port_flow_tunnel *pft; + struct rte_flow_error error; + + port = &ports[port_id]; + pft = port_flow_locate_tunnel_id(port, tunnel_ops->id); + if (!pft) { + printf("failed to locate port flow tunnel #%u\n", + tunnel_ops->id); + return NULL; + } + if (tunnel_ops->actions) { + uint32_t num_actions; + const struct rte_flow_action *aptr; + + ret = rte_flow_tunnel_decap_set(port_id, &pft->tunnel, + &pft->pmd_actions, + &pft->num_pmd_actions, + &error); + if (ret) { + port_flow_complain(&error); + return NULL; + } + for (aptr = actions, num_actions = 1; + aptr->type != RTE_FLOW_ACTION_TYPE_END; + aptr++, num_actions++); + pft->actions = malloc( + (num_actions + pft->num_pmd_actions) * + sizeof(actions[0])); + if (!pft->actions) { + rte_flow_tunnel_action_decap_release( + port_id, pft->actions, + pft->num_pmd_actions, &error); + return NULL; + } + rte_memcpy(pft->actions, pft->pmd_actions, + pft->num_pmd_actions * sizeof(actions[0])); + rte_memcpy(pft->actions + pft->num_pmd_actions, actions, + num_actions * sizeof(actions[0])); + } + if (tunnel_ops->items) { + uint32_t num_items; + const struct rte_flow_item *iptr; + + ret = rte_flow_tunnel_match(port_id, &pft->tunnel, + &pft->pmd_items, + &pft->num_pmd_items, + &error); + if (ret) { + port_flow_complain(&error); + return NULL; + } + for (iptr = pattern, num_items = 1; + iptr->type != RTE_FLOW_ITEM_TYPE_END; + iptr++, num_items++); + pft->items = malloc((num_items + pft->num_pmd_items) * + sizeof(pattern[0])); + if (!pft->items) { + rte_flow_tunnel_item_release( + port_id, pft->pmd_items, + pft->num_pmd_items, &error); + return NULL; + } + rte_memcpy(pft->items, pft->pmd_items, + pft->num_pmd_items * sizeof(pattern[0])); + rte_memcpy(pft->items + pft->num_pmd_items, pattern, + num_items * sizeof(pattern[0])); + } + + return pft; +} + +static void +port_flow_tunnel_offload_cmd_release(portid_t port_id, + const struct tunnel_ops *tunnel_ops, + struct port_flow_tunnel *pft) +{ + struct rte_flow_error error; + + if (tunnel_ops->actions) { + free(pft->actions); + rte_flow_tunnel_action_decap_release( + port_id, pft->pmd_actions, + pft->num_pmd_actions, &error); + pft->actions = NULL; + pft->pmd_actions = NULL; + } + if (tunnel_ops->items) { + free(pft->items); + rte_flow_tunnel_item_release(port_id, pft->pmd_items, + pft->num_pmd_items, + &error); + pft->items = NULL; + pft->pmd_items = NULL; + } +} /** Validate flow rule. */ int port_flow_validate(portid_t port_id, const struct rte_flow_attr *attr, const struct rte_flow_item *pattern, - const struct rte_flow_action *actions) + const struct rte_flow_action *actions, + const struct tunnel_ops *tunnel_ops) { struct rte_flow_error error; + struct port_flow_tunnel *pft = NULL; /* Poisoning to make sure PMDs update it in case of error. */ memset(&error, 0x11, sizeof(error)); + if (tunnel_ops->enabled) { + pft = port_flow_tunnel_offload_cmd_prep(port_id, pattern, + actions, tunnel_ops); + if (!pft) + return -ENOENT; + if (pft->items) + pattern = pft->items; + if (pft->actions) + actions = pft->actions; + } if (rte_flow_validate(port_id, attr, pattern, actions, &error)) return port_flow_complain(&error); + if (tunnel_ops->enabled) + port_flow_tunnel_offload_cmd_release(port_id, tunnel_ops, pft); printf("Flow rule validated\n"); return 0; } @@ -1903,13 +2129,15 @@ int port_flow_create(portid_t port_id, const struct rte_flow_attr *attr, const struct rte_flow_item *pattern, - const struct rte_flow_action *actions) + const struct rte_flow_action *actions, + const struct tunnel_ops *tunnel_ops) { struct rte_flow *flow; struct rte_port *port; struct port_flow *pf; uint32_t id = 0; struct rte_flow_error error; + struct port_flow_tunnel *pft = NULL; port = &ports[port_id]; if (port->flow_list) { @@ -1920,6 +2148,16 @@ port_flow_create(portid_t port_id, } id = port->flow_list->id + 1; } + if (tunnel_ops->enabled) { + pft = port_flow_tunnel_offload_cmd_prep(port_id, pattern, + actions, tunnel_ops); + if (!pft) + return -ENOENT; + if (pft->items) + pattern = pft->items; + if (pft->actions) + actions = pft->actions; + } pf = port_flow_new(attr, pattern, actions, &error); if (!pf) return port_flow_complain(&error); @@ -1935,6 +2173,8 @@ port_flow_create(portid_t port_id, pf->id = id; pf->flow = flow; port->flow_list = pf; + if (tunnel_ops->enabled) + port_flow_tunnel_offload_cmd_release(port_id, tunnel_ops, pft); printf("Flow rule #%u created\n", pf->id); return 0; } @@ -2244,7 +2484,9 @@ port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group) pf->rule.attr->egress ? 'e' : '-', pf->rule.attr->transfer ? 't' : '-'); while (item->type != RTE_FLOW_ITEM_TYPE_END) { - if (rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_NAME_PTR, + if ((uint32_t)item->type > INT_MAX) + name = "PMD_INTERNAL"; + else if (rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_NAME_PTR, &name, sizeof(name), (void *)(uintptr_t)item->type, NULL) <= 0) @@ -2255,7 +2497,9 @@ port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group) } printf("=>"); while (action->type != RTE_FLOW_ACTION_TYPE_END) { - if (rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR, + if ((uint32_t)action->type > INT_MAX) + name = "PMD_INTERNAL"; + else if (rte_flow_conv(RTE_FLOW_CONV_OP_ACTION_NAME_PTR, &name, sizeof(name), (void *)(uintptr_t)action->type, NULL) <= 0) diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 6caba60988..333904d686 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -3684,6 +3684,8 @@ init_port_dcb_config(portid_t pid, static void init_port(void) { + int i; + /* Configuration of Ethernet ports. */ ports = rte_zmalloc("testpmd: ports", sizeof(struct rte_port) * RTE_MAX_ETHPORTS, @@ -3693,7 +3695,8 @@ init_port(void) "rte_zmalloc(%d struct rte_port) failed\n", RTE_MAX_ETHPORTS); } - + for (i = 0; i < RTE_MAX_ETHPORTS; i++) + LIST_INIT(&ports[i].flow_tunnel_list); /* Initialize ports NUMA structures */ memset(port_numa, NUMA_NO_CONFIG, RTE_MAX_ETHPORTS); memset(rxring_numa, NUMA_NO_CONFIG, RTE_MAX_ETHPORTS); diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index f8b0a3517d..5238ac3dd5 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -12,6 +12,7 @@ #include #include #include +#include #define RTE_PORT_ALL (~(portid_t)0x0) @@ -150,6 +151,26 @@ struct port_shared_action { struct rte_flow_shared_action *action; /**< Shared action handle. */ }; +struct port_flow_tunnel { + LIST_ENTRY(port_flow_tunnel) chain; + struct rte_flow_action *pmd_actions; + struct rte_flow_item *pmd_items; + uint32_t id; + uint32_t num_pmd_actions; + uint32_t num_pmd_items; + struct rte_flow_tunnel tunnel; + struct rte_flow_action *actions; + struct rte_flow_item *items; +}; + +struct tunnel_ops { + uint32_t id; + char type[16]; + uint32_t enabled:1; + uint32_t actions:1; + uint32_t items:1; +}; + /** * The data structure associated with each port. */ @@ -182,6 +203,7 @@ struct rte_port { struct port_flow *flow_list; /**< Associated flows. */ struct port_shared_action *actions_list; /**< Associated shared actions. */ + LIST_HEAD(, port_flow_tunnel) flow_tunnel_list; const struct rte_eth_rxtx_callback *rx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1]; const struct rte_eth_rxtx_callback *tx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1]; /**< metadata value to insert in Tx packets. */ @@ -773,11 +795,13 @@ int port_shared_action_update(portid_t port_id, uint32_t id, int port_flow_validate(portid_t port_id, const struct rte_flow_attr *attr, const struct rte_flow_item *pattern, - const struct rte_flow_action *actions); + const struct rte_flow_action *actions, + const struct tunnel_ops *tunnel_ops); int port_flow_create(portid_t port_id, const struct rte_flow_attr *attr, const struct rte_flow_item *pattern, - const struct rte_flow_action *actions); + const struct rte_flow_action *actions, + const struct tunnel_ops *tunnel_ops); int port_shared_action_query(portid_t port_id, uint32_t id); void update_age_action_context(const struct rte_flow_action *actions, struct port_flow *pf); @@ -788,6 +812,12 @@ int port_flow_query(portid_t port_id, uint32_t rule, const struct rte_flow_action *action); void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group); void port_flow_aged(portid_t port_id, uint8_t destroy); +const char *port_flow_tunnel_type(struct rte_flow_tunnel *tunnel); +struct port_flow_tunnel * +port_flow_locate_tunnel(uint16_t port_id, struct rte_flow_tunnel *tun); +void port_flow_tunnel_list(portid_t port_id); +void port_flow_tunnel_destroy(portid_t port_id, uint32_t tunnel_id); +void port_flow_tunnel_create(portid_t port_id, const struct tunnel_ops *ops); int port_flow_isolate(portid_t port_id, int set); void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id); diff --git a/app/test-pmd/util.c b/app/test-pmd/util.c index 8488fa1a8f..781a813759 100644 --- a/app/test-pmd/util.c +++ b/app/test-pmd/util.c @@ -48,18 +48,49 @@ dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[], is_rx ? "received" : "sent", (unsigned int) nb_pkts); for (i = 0; i < nb_pkts; i++) { + int ret; + struct rte_flow_error error; + struct rte_flow_restore_info info = { 0, }; + mb = pkts[i]; eth_hdr = rte_pktmbuf_read(mb, 0, sizeof(_eth_hdr), &_eth_hdr); eth_type = RTE_BE_TO_CPU_16(eth_hdr->ether_type); - ol_flags = mb->ol_flags; packet_type = mb->packet_type; is_encapsulation = RTE_ETH_IS_TUNNEL_PKT(packet_type); - + ret = rte_flow_get_restore_info(port_id, mb, &info, &error); + if (!ret) { + printf("restore info:"); + if (info.flags & RTE_FLOW_RESTORE_INFO_TUNNEL) { + struct port_flow_tunnel *port_tunnel; + + port_tunnel = port_flow_locate_tunnel + (port_id, &info.tunnel); + printf(" - tunnel"); + if (port_tunnel) + printf(" #%u", port_tunnel->id); + else + printf(" %s", "-none-"); + printf(" type %s", + port_flow_tunnel_type(&info.tunnel)); + } else { + printf(" - no tunnel info"); + } + if (info.flags & RTE_FLOW_RESTORE_INFO_ENCAPSULATED) + printf(" - outer header present"); + else + printf(" - no outer header"); + if (info.flags & RTE_FLOW_RESTORE_INFO_GROUP_ID) + printf(" - miss group %u", info.group_id); + else + printf(" - no miss group"); + printf("\n"); + } print_ether_addr(" src=", ð_hdr->s_addr); print_ether_addr(" - dst=", ð_hdr->d_addr); printf(" - type=0x%04x - length=%u - nb_segs=%d", eth_type, (unsigned int) mb->pkt_len, (int)mb->nb_segs); + ol_flags = mb->ol_flags; if (ol_flags & PKT_RX_RSS_HASH) { printf(" - RSS hash=0x%x", (unsigned int) mb->hash.rss); printf(" - RSS queue=0x%x", (unsigned int) queue); diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst index 43c0ea0599..05a4446757 100644 --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst @@ -3749,6 +3749,45 @@ following sections. flow aged {port_id} [destroy] +- Tunnel offload - create a tunnel stub:: + + flow tunnel create {port_id} type {tunnel_type} + +- Tunnel offload - destroy a tunnel stub:: + + flow tunnel destroy {port_id} id {tunnel_id} + +- Tunnel offload - list port tunnel stubs:: + + flow tunnel list {port_id} + +Creating a tunnel stub for offload +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``flow tunnel create`` setup a tunnel stub for tunnel offload flow rules:: + + flow tunnel create {port_id} type {tunnel_type} + +If successful, it will return a tunnel stub ID usable with other commands:: + + port [...]: flow tunnel #[...] type [...] + +Tunnel stub ID is relative to a port. + +Destroying tunnel offload stub +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``flow tunnel destroy`` destroy port tunnel stub:: + + flow tunnel destroy {port_id} id {tunnel_id} + +Listing tunnel offload stubs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``flow tunnel list`` list port tunnel offload stubs:: + + flow tunnel list {port_id} + Validating flow rules ~~~~~~~~~~~~~~~~~~~~~ @@ -3795,6 +3834,7 @@ to ``rte_flow_create()``:: flow create {port_id} [group {group_id}] [priority {level}] [ingress] [egress] [transfer] + [tunnel_set {tunnel_id}] [tunnel_match {tunnel_id}] pattern {item} [/ {item} [...]] / end actions {action} [/ {action} [...]] / end @@ -3809,6 +3849,7 @@ Otherwise it will show an error message of the form:: Parameters describe in the following order: - Attributes (*group*, *priority*, *ingress*, *egress*, *transfer* tokens). +- Tunnel offload specification (tunnel_set, tunnel_match) - A matching pattern, starting with the *pattern* token and terminated by an *end* pattern item. - Actions, starting with the *actions* token and terminated by an *end* @@ -3852,6 +3893,14 @@ Most rules affect RX therefore contain the ``ingress`` token:: testpmd> flow create 0 ingress pattern [...] +Tunnel offload +^^^^^^^^^^^^^^ + +Indicate tunnel offload rule type + +- ``tunnel_set {tunnel_id}``: mark rule as tunnel offload decap_set type. +- ``tunnel_match {tunnel_id}``: mark rule as tunel offload match type. + Matching pattern ^^^^^^^^^^^^^^^^