Skip to contentSkip to conversation

Generic flow director/filtering/classification API

RFC In review Adrien Mazarguil 21 participants 260 messages Jul 5, 2016 – Nov 14, 2017 Page 2 of 4

lore
Share & tools
lore
#63 lore raw

PATCH 1/22 ethdev: introduce generic flow API

In reply to Adrien Mazarguil

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

---
 MAINTAINERS                            |   4 +
 lib/librte_ether/Makefile              |   3 +
 lib/librte_ether/rte_eth_ctrl.h        |   1 +
 lib/librte_ether/rte_ether_version.map |  10 +
 lib/librte_ether/rte_flow.c            | 159 +++++
 lib/librte_ether/rte_flow.h            | 947 ++++++++++++++++++++++++++++
 lib/librte_ether/rte_flow_driver.h     | 177 ++++++
 7 files changed, 1301 insertions(+)
MAINTAINERS +4 -0
index d6bb8f8..3b46630 100644
@@ -243,6 +243,10 @@ M: Thomas Monjalon <[email protected]>
 F: lib/librte_ether/
 F: scripts/test-null.sh
 
+Generic flow API
+M: Adrien Mazarguil <[email protected]>
+F: lib/librte_ether/rte_flow*
+
 Crypto API
 M: Declan Doherty <[email protected]>
 F: lib/librte_cryptodev/
lib/librte_ether/Makefile +3 -0
index efe1e5f..9335361 100644
@@ -44,6 +44,7 @@ EXPORT_MAP := rte_ether_version.map
 LIBABIVER := 5
 
 SRCS-y += rte_ethdev.c
+SRCS-y += rte_flow.c
 
 #
 # Export include files
@@ -51,6 +52,8 @@ SRCS-y += rte_ethdev.c
 SYMLINK-y-include += rte_ethdev.h
 SYMLINK-y-include += rte_eth_ctrl.h
 SYMLINK-y-include += rte_dev_info.h
+SYMLINK-y-include += rte_flow.h
+SYMLINK-y-include += rte_flow_driver.h
 
 # this lib depends upon:
 DEPDIRS-y += lib/librte_net lib/librte_eal lib/librte_mempool lib/librte_ring lib/librte_mbuf
lib/librte_ether/rte_eth_ctrl.h +1 -0
index fe80eb0..8386904 100644
@@ -99,6 +99,7 @@ enum rte_filter_type {
 	RTE_ETH_FILTER_FDIR,
 	RTE_ETH_FILTER_HASH,
 	RTE_ETH_FILTER_L2_TUNNEL,
+	RTE_ETH_FILTER_GENERIC,
 	RTE_ETH_FILTER_MAX
 };
 
lib/librte_ether/rte_ether_version.map +10 -0
index 72be66d..b5d2547 100644
@@ -147,3 +147,13 @@ DPDK_16.11 {
 	rte_eth_dev_pci_remove;
 
 } DPDK_16.07;
+
+DPDK_17.02 {
+	global:
+
+	rte_flow_validate;
+	rte_flow_create;
+	rte_flow_destroy;
+	rte_flow_query;
+
+} DPDK_16.11;
lib/librte_ether/rte_flow.c new file +159 -0

159 lines not on this page · raw message

lib/librte_ether/rte_flow.h new file +947 -0

947 lines not on this page · raw message

lib/librte_ether/rte_flow_driver.h new file +177 -0

177 lines not on this page · raw message

Signature · 1 line
2.1.4
#64 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Adrien,

Quoted 31 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Thursday, November 17, 2016 12:23 AM
To: [email protected]
Cc: Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>
Subject: [dpdk-dev] [PATCH 01/22] ethdev: introduce generic flow API

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

lib/librte_ether/rte_flow.h line 757

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+
+/**
+ * Opaque type returned after successfully creating a flow.
+ *
+ * This handle can be used to manage and query the related flow (e.g.
+to
+ * destroy it or retrieve counters).
+ */
+struct rte_flow;
+

As we talked before, we use attr/pattern/actions to create and destroy a flow in PMD, but I don't think it's easy to clone the user-provided parameters and return the result to the application as a rte_flow pointer. As you suggested:
/* PMD-specific code. */

 struct rte_flow {
    struct rte_flow_attr attr;
    struct rte_flow_item *pattern;
    struct rte_flow_action *actions;

};

Because both pattern and actions are pointers, and there're also pointers in structure rte_flow_item and struct rte_flow_action. We need to iterate allocation during clone and iterate free during destroy, then seems that the code is something ugly, right?

I think application saves info when creating a flow rule, so why not application provide attr/pattern/actions info to PMD before calling PMD API?

Thanks,
Beilei Xing

#65 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Beilei,

Quoted 48 lines · Beilei

Hi Adrien,

Quoted 31 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Thursday, November 17, 2016 12:23 AM
To: [email protected]
Cc: Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>
Subject: [dpdk-dev] [PATCH 01/22] ethdev: introduce generic flow API

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

lib/librte_ether/rte_flow.h line 757

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+
+/**
+ * Opaque type returned after successfully creating a flow.
+ *
+ * This handle can be used to manage and query the related flow (e.g.
+to
+ * destroy it or retrieve counters).
+ */
+struct rte_flow;
+

As we talked before, we use attr/pattern/actions to create and destroy a flow in PMD, but I don't think it's easy to clone the user-provided parameters and return the result to the application as a rte_flow pointer. As you suggested:
/* PMD-specific code. */

Quoted 5 lines
 struct rte_flow {
    struct rte_flow_attr attr;
    struct rte_flow_item *pattern;
    struct rte_flow_action *actions;

};

Just to provide some context to the community since the above snippet comes from private exchanges, I've suggested the above structure as a mean to create and remove rules in the same fashion as FDIR, by providing the rule used for creation to the destroy callback.

As an opaque type, each PMD currently needs to implement its own version of struct rte_flow. The above definition may ease transition from FDIR to rte_flow for some PMDs, however they need to clone the entire application-provided rule to do so because there is no requirement for it to be kept allocated.

I've implemented such a function in testpmd (port_flow_new() in commit [1]) as an example.

[1] http://dpdk.org/ml/archives/dev/2016-November/050266.html

However my suggestion is for PMDs to use their own HW-specific structure that only contains relevant information instead of being forced to drag large, non-native data around, missing useful context and that requires parsing every time. This is one benefit of using an opaque type in the first place, the other being ABI breakage avoidance.

Quoted 3 lines

Because both pattern and actions are pointers, and there're also pointers in structure rte_flow_item and struct rte_flow_action. We need to iterate allocation during clone and iterate free during destroy, then seems that the code is something ugly, right?

Well since I wrote that code, I won't easily admit it's ugly. I think PMDs should not require the duplication of generic rules actually, which are only defined as a common language between applications and PMDs. Both are free to store rules in their own preferred and efficient format internally.

Quoted 2 lines

I think application saves info when creating a flow rule, so why not application provide attr/pattern/actions info to PMD before calling PMD API?

They have to do so temporarily (e.g. allocated on the stack) while calling rte_flow_create() and rte_flow_validate(), that's it. Once a rule is created, there's no requirement for applications to keep anything around.

For simple applications such as testpmd, the generic format is probably enough. More complex and existing applications such as ovs-dpdk may rather choose to keep using their internal format that already fits their needs, partially duplicating this information in rte_flow_attr and rte_flow_item/rte_flow_action lists would waste memory. The conversion in this case should only be performed when creating/validating flow rules.

In short, I fail to see any downside with maintaining struct rte_flow opaque to applications.

Best regards,

Signature · 2 lines
Adrien Mazarguil
6WIND
#66 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

In reply to Adrien Mazarguil

Hi Adrien,

Quoted 10 lines · Adrien Mazarguil

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

Quoted 8 lines

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

I'd suggest to add a deprecation notice to deprecation.rst, ideally with a target release.

Quoted 84 lines
---
 MAINTAINERS                            |   4 +
 lib/librte_ether/Makefile              |   3 +
 lib/librte_ether/rte_eth_ctrl.h        |   1 +
 lib/librte_ether/rte_ether_version.map |  10 +
 lib/librte_ether/rte_flow.c            | 159 +++++
 lib/librte_ether/rte_flow.h            | 947 ++++++++++++++++++++++++++++
 lib/librte_ether/rte_flow_driver.h     | 177 ++++++
 7 files changed, 1301 insertions(+)
MAINTAINERS +4 -0
index d6bb8f8..3b46630 100644
@@ -243,6 +243,10 @@ M: Thomas Monjalon <[email protected]>
 F: lib/librte_ether/
 F: scripts/test-null.sh
 
+Generic flow API
+M: Adrien Mazarguil <[email protected]>
+F: lib/librte_ether/rte_flow*
+
 Crypto API
 M: Declan Doherty <[email protected]>
 F: lib/librte_cryptodev/
lib/librte_ether/Makefile +3 -0
index efe1e5f..9335361 100644
@@ -44,6 +44,7 @@ EXPORT_MAP := rte_ether_version.map
 LIBABIVER := 5
 
 SRCS-y += rte_ethdev.c
+SRCS-y += rte_flow.c
 
 #
 # Export include files
@@ -51,6 +52,8 @@ SRCS-y += rte_ethdev.c
 SYMLINK-y-include += rte_ethdev.h
 SYMLINK-y-include += rte_eth_ctrl.h
 SYMLINK-y-include += rte_dev_info.h
+SYMLINK-y-include += rte_flow.h
+SYMLINK-y-include += rte_flow_driver.h
 
 # this lib depends upon:
 DEPDIRS-y += lib/librte_net lib/librte_eal lib/librte_mempool lib/librte_ring lib/librte_mbuf
lib/librte_ether/rte_eth_ctrl.h +1 -0
index fe80eb0..8386904 100644
@@ -99,6 +99,7 @@ enum rte_filter_type {
 	RTE_ETH_FILTER_FDIR,
 	RTE_ETH_FILTER_HASH,
 	RTE_ETH_FILTER_L2_TUNNEL,
+	RTE_ETH_FILTER_GENERIC,
 	RTE_ETH_FILTER_MAX
 };
 
lib/librte_ether/rte_ether_version.map +10 -0
index 72be66d..b5d2547 100644
@@ -147,3 +147,13 @@ DPDK_16.11 {
 	rte_eth_dev_pci_remove;
 
 } DPDK_16.07;
+
+DPDK_17.02 {
+	global:
+
+	rte_flow_validate;
+	rte_flow_create;
+	rte_flow_destroy;
+	rte_flow_query;
+
+} DPDK_16.11;
lib/librte_ether/rte_flow.c new file +0 -0
new file mode 100644
index 0000000..064963d

lib/librte_ether/rte_flow.c line 5

Quoted 5 lines
lib/librte_ether/rte_flow.c new file +5 -0
@@ -0,0 +1,159 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2016 6WIND S.A.
+ *   Copyright 2016 Mellanox.

There's Mellanox copyright but you are the only signed-off-by - is that right?

Quoted 50 lines
lib/librte_ether/rte_flow.c new file +50 -0
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of 6WIND S.A. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+#include <rte_errno.h>
+#include <rte_branch_prediction.h>
+#include "rte_ethdev.h"
+#include "rte_flow_driver.h"
+#include "rte_flow.h"
+
+/* Get generic flow operations structure from a port. */
+const struct rte_flow_ops *
+rte_flow_ops_get(uint8_t port_id, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops;
+	int code;
+
+	if (unlikely(!rte_eth_dev_is_valid_port(port_id)))
+		code = ENODEV;
+	else if (unlikely(!dev->dev_ops->filter_ctrl ||
+			  dev->dev_ops->filter_ctrl(dev,
+						    RTE_ETH_FILTER_GENERIC,
+						    RTE_ETH_FILTER_GET,

lib/librte_ether/rte_flow.c line 65

Quoted 10 lines
lib/librte_ether/rte_flow.c new file +10 -0
+						    &ops) ||
+			  !ops))
+		code = ENOTSUP;
+	else
+		return ops;
+	rte_flow_error_set(error, code, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(code));
+	return NULL;
+}
+

Is it expected that the application or pmd will provide locking between these functions if required? I think it's going to have to be the app.

Quoted 167 lines
lib/librte_ether/rte_flow.c new file +94 -0
+/* Check whether a flow rule can be created on a given port. */
+int
+rte_flow_validate(uint8_t port_id,
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error)
+{
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->validate))
+		return ops->validate(dev, attr, pattern, actions, error);
+	rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(ENOTSUP));
+	return -rte_errno;
+}
+
+/* Create a flow rule on a given port. */
+struct rte_flow *
+rte_flow_create(uint8_t port_id,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action 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 NULL;
+	if (likely(!!ops->create))
+		return ops->create(dev, attr, pattern, actions, error);
+	rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(ENOTSUP));
+	return NULL;
+}
+
+/* Destroy a flow rule on a given port. */
+int
+rte_flow_destroy(uint8_t port_id,
+		 struct rte_flow *flow,
+		 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->destroy))
+		return ops->destroy(dev, flow, error);
+	rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(ENOTSUP));
+	return -rte_errno;
+}
+
+/* Destroy all flow rules associated with a port. */
+int
+rte_flow_flush(uint8_t port_id,
+	       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->flush))
+		return ops->flush(dev, error);
+	rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(ENOTSUP));
+	return -rte_errno;
+}
+
+/* Query an existing flow rule. */
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       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 (!ops)
+		return -rte_errno;
+	if (likely(!!ops->query))
+		return ops->query(dev, flow, action, data, error);
+	rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(ENOTSUP));
+	return -rte_errno;
+}
lib/librte_ether/rte_flow.h new file +67 -0
new file mode 100644
index 0000000..211f307
@@ -0,0 +1,947 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2016 6WIND S.A.
+ *   Copyright 2016 Mellanox.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of 6WIND S.A. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RTE_FLOW_H_
+#define RTE_FLOW_H_
+
+/**
+ * @file
+ * RTE generic flow API
+ *
+ * This interface provides the ability to program packet matching and
+ * associated actions in hardware through flow rules.
+ */
+
+#include <rte_arp.h>
+#include <rte_ether.h>
+#include <rte_icmp.h>
+#include <rte_ip.h>
+#include <rte_sctp.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Flow rule attributes.
+ *
+ * Priorities are set on two levels: per group and per rule within groups.
+ *
+ * Lower values denote higher priority, the highest priority for both levels
+ * is 0, so that a rule with priority 0 in group 8 is always matched after a
+ * rule with priority 8 in group 0.
+ *
+ * Although optional, applications are encouraged to group similar rules as
+ * much as possible to fully take advantage of hardware capabilities

lib/librte_ether/rte_flow.h line 77

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ * (e.g. optimized matching) and work around limitations (e.g. a single
+ * pattern type possibly allowed in a given group).
+ *
+ * Group and priority levels are arbitrary and up to the application, they
+ * do not need to be contiguous nor start from 0, however the maximum number
+ * varies between devices and may be affected by existing flow rules.
+ *
+ * If a packet is matched by several rules of a given group for a given
+ * priority level, the outcome is undefined. It can take any path, may be
+ * duplicated or even cause unrecoverable errors.

I get what you are trying to do here wrt supporting multiple pmds/hardware implementations and it's a good idea to keep it flexible.

Given that the outcome is undefined, it would be nice that the application has a way of finding the specific effects for verification and debugging.

Quoted 224 lines
lib/librte_ether/rte_flow.h new file +224 -0
+ *
+ * Note that support for more than a single group and priority level is not
+ * guaranteed.
+ *
+ * Flow rules can apply to inbound and/or outbound traffic (ingress/egress).
+ *
+ * Several pattern items and actions are valid and can be used in both
+ * directions. Those valid for only one direction are described as such.
+ *
+ * Specifying both directions at once is not recommended but may be valid in
+ * some cases, such as incrementing the same counter twice.
+ *
+ * Not specifying any direction is currently an error.
+ */
+struct rte_flow_attr {
+	uint32_t group; /**< Priority group. */
+	uint32_t priority; /**< Priority level within group. */
+	uint32_t ingress:1; /**< Rule applies to ingress traffic. */
+	uint32_t egress:1; /**< Rule applies to egress traffic. */
+	uint32_t reserved:30; /**< Reserved, must be zero. */
+};
+
+/**
+ * Matching pattern item types.
+ *
+ * Items are arranged in a list to form a matching pattern for packets.
+ * They fall in two categories:
+ *
+ * - Protocol matching (ANY, RAW, ETH, IPV4, IPV6, ICMP, UDP, TCP, SCTP,
+ *   VXLAN and so on), usually associated with a specification
+ *   structure. These must be stacked in the same order as the protocol
+ *   layers to match, starting from L2.
+ *
+ * - Affecting how the pattern is processed (END, VOID, INVERT, PF, VF, PORT
+ *   and so on), often without a specification structure. Since they are
+ *   meta data that does not match packet contents, these can be specified
+ *   anywhere within item lists without affecting the protocol matching
+ *   items.
+ *
+ * See the description of individual types for more information. Those
+ * marked with [META] fall into the second category.
+ */
+enum rte_flow_item_type {
+	/**
+	 * [META]
+	 *
+	 * End marker for item lists. Prevents further processing of items,
+	 * thereby ending the pattern.
+	 *
+	 * No associated specification structure.
+	 */
+	RTE_FLOW_ITEM_TYPE_END,
+
+	/**
+	 * [META]
+	 *
+	 * Used as a placeholder for convenience. It is ignored and simply
+	 * discarded by PMDs.
+	 *
+	 * No associated specification structure.
+	 */
+	RTE_FLOW_ITEM_TYPE_VOID,
+
+	/**
+	 * [META]
+	 *
+	 * Inverted matching, i.e. process packets that do not match the
+	 * pattern.
+	 *
+	 * No associated specification structure.
+	 */
+	RTE_FLOW_ITEM_TYPE_INVERT,
+
+	/**
+	 * Matches any protocol in place of the current layer, a single ANY
+	 * may also stand for several protocol layers.
+	 *
+	 * See struct rte_flow_item_any.
+	 */
+	RTE_FLOW_ITEM_TYPE_ANY,
+
+	/**
+	 * [META]
+	 *
+	 * Matches packets addressed to the physical function of the device.
+	 *
+	 * If the underlying device function differs from the one that would
+	 * normally receive the matched traffic, specifying this item
+	 * prevents it from reaching that device unless the flow rule
+	 * contains a PF action. Packets are not duplicated between device
+	 * instances by default.
+	 *
+	 * No associated specification structure.
+	 */
+	RTE_FLOW_ITEM_TYPE_PF,
+
+	/**
+	 * [META]
+	 *
+	 * Matches packets addressed to a virtual function ID of the device.
+	 *
+	 * If the underlying device function differs from the one that would
+	 * normally receive the matched traffic, specifying this item
+	 * prevents it from reaching that device unless the flow rule
+	 * contains a VF action. Packets are not duplicated between device
+	 * instances by default.
+	 *
+	 * See struct rte_flow_item_vf.
+	 */
+	RTE_FLOW_ITEM_TYPE_VF,
+
+	/**
+	 * [META]
+	 *
+	 * Matches packets coming from the specified physical port of the
+	 * underlying device.
+	 *
+	 * The first PORT item overrides the physical port normally
+	 * associated with the specified DPDK input port (port_id). This
+	 * item can be provided several times to match additional physical
+	 * ports.
+	 *
+	 * See struct rte_flow_item_port.
+	 */
+	RTE_FLOW_ITEM_TYPE_PORT,
+
+	/**
+	 * Matches a byte string of a given length at a given offset.
+	 *
+	 * See struct rte_flow_item_raw.
+	 */
+	RTE_FLOW_ITEM_TYPE_RAW,
+
+	/**
+	 * Matches an Ethernet header.
+	 *
+	 * See struct rte_flow_item_eth.
+	 */
+	RTE_FLOW_ITEM_TYPE_ETH,
+
+	/**
+	 * Matches an 802.1Q/ad VLAN tag.
+	 *
+	 * See struct rte_flow_item_vlan.
+	 */
+	RTE_FLOW_ITEM_TYPE_VLAN,
+
+	/**
+	 * Matches an IPv4 header.
+	 *
+	 * See struct rte_flow_item_ipv4.
+	 */
+	RTE_FLOW_ITEM_TYPE_IPV4,
+
+	/**
+	 * Matches an IPv6 header.
+	 *
+	 * See struct rte_flow_item_ipv6.
+	 */
+	RTE_FLOW_ITEM_TYPE_IPV6,
+
+	/**
+	 * Matches an ICMP header.
+	 *
+	 * See struct rte_flow_item_icmp.
+	 */
+	RTE_FLOW_ITEM_TYPE_ICMP,
+
+	/**
+	 * Matches a UDP header.
+	 *
+	 * See struct rte_flow_item_udp.
+	 */
+	RTE_FLOW_ITEM_TYPE_UDP,
+
+	/**
+	 * Matches a TCP header.
+	 *
+	 * See struct rte_flow_item_tcp.
+	 */
+	RTE_FLOW_ITEM_TYPE_TCP,
+
+	/**
+	 * Matches a SCTP header.
+	 *
+	 * See struct rte_flow_item_sctp.
+	 */
+	RTE_FLOW_ITEM_TYPE_SCTP,
+
+	/**
+	 * Matches a VXLAN header.
+	 *
+	 * See struct rte_flow_item_vxlan.
+	 */
+	RTE_FLOW_ITEM_TYPE_VXLAN,
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_ANY
+ *
+ * Matches any protocol in place of the current layer, a single ANY may also
+ * stand for several protocol layers.
+ *
+ * This is usually specified as the first pattern item when looking for a
+ * protocol anywhere in a packet.
+ *
+ * A maximum value of 0 requests matching any number of protocol layers
+ * above or equal to the minimum value, a maximum value lower than the
+ * minimum one is otherwise invalid.
+ *
+ * This type does not work with a range (struct rte_flow_item.last).
+ */
+struct rte_flow_item_any {
+	uint16_t min; /**< Minimum number of layers covered. */
+	uint16_t max; /**< Maximum number of layers covered, 0 for infinity. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_VF
+ *
+ * Matches packets addressed to a virtual function ID of the device.
+ *
+ * If the underlying device function differs from the one that would
+ * normally receive the matched traffic, specifying this item prevents it

lib/librte_ether/rte_flow.h line 311

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ * from reaching that device unless the flow rule contains a VF
+ * action. Packets are not duplicated between device instances by default.
+ *
+ * - Likely to return an error or never match any traffic if this causes a
+ *   VF device to match traffic addressed to a different VF.
+ * - Can be specified multiple times to match traffic addressed to several
+ *   specific VFs.
+ * - Can be combined with a PF item to match both PF and VF traffic.
+ *
+ * A zeroed mask can be used to match any VF.

can you refer explicitly to id

Quoted 160 lines
lib/librte_ether/rte_flow.h new file +160 -0
+ */
+struct rte_flow_item_vf {
+	uint32_t id; /**< Destination VF ID. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_PORT
+ *
+ * Matches packets coming from the specified physical port of the underlying
+ * device.
+ *
+ * The first PORT item overrides the physical port normally associated with
+ * the specified DPDK input port (port_id). This item can be provided
+ * several times to match additional physical ports.
+ *
+ * Note that physical ports are not necessarily tied to DPDK input ports
+ * (port_id) when those are not under DPDK control. Possible values are
+ * specific to each device, they are not necessarily indexed from zero and
+ * may not be contiguous.
+ *
+ * As a device property, the list of allowed values as well as the value
+ * associated with a port_id should be retrieved by other means.
+ *
+ * A zeroed mask can be used to match any port index.
+ */
+struct rte_flow_item_port {
+	uint32_t index; /**< Physical port index. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_RAW
+ *
+ * Matches a byte string of a given length at a given offset.
+ *
+ * Offset is either absolute (using the start of the packet) or relative to
+ * the end of the previous matched item in the stack, in which case negative
+ * values are allowed.
+ *
+ * If search is enabled, offset is used as the starting point. The search
+ * area can be delimited by setting limit to a nonzero value, which is the
+ * maximum number of bytes after offset where the pattern may start.
+ *
+ * Matching a zero-length pattern is allowed, doing so resets the relative
+ * offset for subsequent items.
+ *
+ * This type does not work with a range (struct rte_flow_item.last).
+ */
+struct rte_flow_item_raw {
+	uint32_t relative:1; /**< Look for pattern after the previous item. */
+	uint32_t search:1; /**< Search pattern from offset (see also limit). */
+	uint32_t reserved:30; /**< Reserved, must be set to zero. */
+	int32_t offset; /**< Absolute or relative offset for pattern. */
+	uint16_t limit; /**< Search area limit for start of pattern. */
+	uint16_t length; /**< Pattern length. */
+	uint8_t pattern[]; /**< Byte string to look for. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_ETH
+ *
+ * Matches an Ethernet header.
+ */
+struct rte_flow_item_eth {
+	struct ether_addr dst; /**< Destination MAC. */
+	struct ether_addr src; /**< Source MAC. */
+	unsigned int type; /**< EtherType. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_VLAN
+ *
+ * Matches an 802.1Q/ad VLAN tag.
+ *
+ * This type normally follows either RTE_FLOW_ITEM_TYPE_ETH or
+ * RTE_FLOW_ITEM_TYPE_VLAN.
+ */
+struct rte_flow_item_vlan {
+	uint16_t tpid; /**< Tag protocol identifier. */
+	uint16_t tci; /**< Tag control information. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_IPV4
+ *
+ * Matches an IPv4 header.
+ *
+ * Note: IPv4 options are handled by dedicated pattern items.
+ */
+struct rte_flow_item_ipv4 {
+	struct ipv4_hdr hdr; /**< IPv4 header definition. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_IPV6.
+ *
+ * Matches an IPv6 header.
+ *
+ * Note: IPv6 options are handled by dedicated pattern items.
+ */
+struct rte_flow_item_ipv6 {
+	struct ipv6_hdr hdr; /**< IPv6 header definition. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_ICMP.
+ *
+ * Matches an ICMP header.
+ */
+struct rte_flow_item_icmp {
+	struct icmp_hdr hdr; /**< ICMP header definition. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_UDP.
+ *
+ * Matches a UDP header.
+ */
+struct rte_flow_item_udp {
+	struct udp_hdr hdr; /**< UDP header definition. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_TCP.
+ *
+ * Matches a TCP header.
+ */
+struct rte_flow_item_tcp {
+	struct tcp_hdr hdr; /**< TCP header definition. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_SCTP.
+ *
+ * Matches a SCTP header.
+ */
+struct rte_flow_item_sctp {
+	struct sctp_hdr hdr; /**< SCTP header definition. */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_VXLAN.
+ *
+ * Matches a VXLAN header (RFC 7348).
+ */
+struct rte_flow_item_vxlan {
+	uint8_t flags; /**< Normally 0x08 (I flag). */
+	uint8_t rsvd0[3]; /**< Reserved, normally 0x000000. */
+	uint8_t vni[3]; /**< VXLAN identifier. */
+	uint8_t rsvd1; /**< Reserved, normally 0x00. */
+};
+
+/**
+ * Matching pattern item definition.
+ *
+ * A pattern is formed by stacking items starting from the lowest protocol
+ * layer to match. This stacking restriction does not apply to meta items
+ * which can be placed anywhere in the stack with no effect on the meaning
+ * of the resulting pattern.
+ *
+ * A stack is terminated by a END item.

lib/librte_ether/rte_flow.h line 481

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ *
+ * The spec field should be a valid pointer to a structure of the related
+ * item type. It may be set to NULL in many cases to use default values.
+ *
+ * Optionally, last can point to a structure of the same type to define an
+ * inclusive range. This is mostly supported by integer and address fields,
+ * may cause errors otherwise. Fields that do not support ranges must be set
+ * to the same value as their spec counterparts.
+ *
+ * By default all fields present in spec are considered relevant.* This

typo "*"

Quoted 36 lines
lib/librte_ether/rte_flow.h new file +36 -0
+ * behavior can be altered by providing a mask structure of the same type
+ * with applicable bits set to one. It can also be used to partially filter
+ * out specific fields (e.g. as an alternate mean to match ranges of IP
+ * addresses).
+ *
+ * Note this is a simple bit-mask applied before interpreting the contents
+ * of spec and last, which may yield unexpected results if not used
+ * carefully. For example, if for an IPv4 address field, spec provides
+ * 10.1.2.3, last provides 10.3.4.5 and mask provides 255.255.0.0, the
+ * effective range is 10.1.0.0 to 10.3.255.255.
+ *
+ * * The defaults for data-matching items such as IPv4 when mask is not
+ *   specified actually depend on the underlying implementation since only
+ *   recognized fields can be taken into account.
+ */
+struct rte_flow_item {
+	enum rte_flow_item_type type; /**< Item type. */
+	const void *spec; /**< Pointer to item specification structure. */
+	const void *last; /**< Defines an inclusive range (spec to last). */
+	const void *mask; /**< Bit-mask applied to spec and last. */
+};
+
+/**
+ * Action types.
+ *
+ * Each possible action is represented by a type. Some have associated
+ * configuration structures. Several actions combined in a list can be
+ * affected to a flow rule. That list is not ordered.
+ *
+ * They fall in three categories:
+ *
+ * - Terminating actions (such as QUEUE, DROP, RSS, PF, VF) that prevent
+ *   processing matched packets by subsequent flow rules, unless overridden
+ *   with PASSTHRU.
+ *
+ * - Non terminating actions (PASSTHRU, DUP) that leave matched packets up

lib/librte_ether/rte_flow.h line 527

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ *   for additional processing by subsequent flow rules.
+ *
+ * - Other non terminating meta actions that do not affect the fate of
+ *   packets (END, VOID, MARK, FLAG, COUNT).
+ *
+ * When several actions are combined in a flow rule, they should all have
+ * different types (e.g. dropping a packet twice is not possible). The
+ * defined behavior is for PMDs to only take into account the last action of
+ * a given type found in the list. PMDs still perform error checking on the
+ * entire list.

why do you define that the pmd will interpret multiple same type rules in this way...would it not make more sense for the pmd to just return EINVAL for an invalid set of rules? It seems more transparent for the application.

Quoted 121 lines
lib/librte_ether/rte_flow.h new file +121 -0
+ *
+ * Note that PASSTHRU is the only action able to override a terminating
+ * rule.
+ */
+enum rte_flow_action_type {
+	/**
+	 * [META]
+	 *
+	 * End marker for action lists. Prevents further processing of
+	 * actions, thereby ending the list.
+	 *
+	 * No associated configuration structure.
+	 */
+	RTE_FLOW_ACTION_TYPE_END,
+
+	/**
+	 * [META]
+	 *
+	 * Used as a placeholder for convenience. It is ignored and simply
+	 * discarded by PMDs.
+	 *
+	 * No associated configuration structure.
+	 */
+	RTE_FLOW_ACTION_TYPE_VOID,
+
+	/**
+	 * Leaves packets up for additional processing by subsequent flow
+	 * rules. This is the default when a rule does not contain a
+	 * terminating action, but can be specified to force a rule to
+	 * become non-terminating.
+	 *
+	 * No associated configuration structure.
+	 */
+	RTE_FLOW_ACTION_TYPE_PASSTHRU,
+
+	/**
+	 * [META]
+	 *
+	 * Attaches a 32 bit value to packets.
+	 *
+	 * See struct rte_flow_action_mark.
+	 */
+	RTE_FLOW_ACTION_TYPE_MARK,
+
+	/**
+	 * [META]
+	 *
+	 * Flag packets. Similar to MARK but only affects ol_flags.
+	 *
+	 * Note: a distinctive flag must be defined for it.
+	 *
+	 * No associated configuration structure.
+	 */
+	RTE_FLOW_ACTION_TYPE_FLAG,
+
+	/**
+	 * Assigns packets to a given queue index.
+	 *
+	 * See struct rte_flow_action_queue.
+	 */
+	RTE_FLOW_ACTION_TYPE_QUEUE,
+
+	/**
+	 * Drops packets.
+	 *
+	 * PASSTHRU overrides this action if both are specified.
+	 *
+	 * No associated configuration structure.
+	 */
+	RTE_FLOW_ACTION_TYPE_DROP,
+
+	/**
+	 * [META]
+	 *
+	 * Enables counters for this rule.
+	 *
+	 * These counters can be retrieved and reset through rte_flow_query(),
+	 * see struct rte_flow_query_count.
+	 *
+	 * No associated configuration structure.
+	 */
+	RTE_FLOW_ACTION_TYPE_COUNT,
+
+	/**
+	 * Duplicates packets to a given queue index.
+	 *
+	 * This is normally combined with QUEUE, however when used alone, it
+	 * is actually similar to QUEUE + PASSTHRU.
+	 *
+	 * See struct rte_flow_action_dup.
+	 */
+	RTE_FLOW_ACTION_TYPE_DUP,
+
+	/**
+	 * Similar to QUEUE, except RSS is additionally performed on packets
+	 * to spread them among several queues according to the provided
+	 * parameters.
+	 *
+	 * See struct rte_flow_action_rss.
+	 */
+	RTE_FLOW_ACTION_TYPE_RSS,
+
+	/**
+	 * Redirects packets to the physical function (PF) of the current
+	 * device.
+	 *
+	 * No associated configuration structure.
+	 */
+	RTE_FLOW_ACTION_TYPE_PF,
+
+	/**
+	 * Redirects packets to the virtual function (VF) of the current
+	 * device with the specified ID.
+	 *
+	 * See struct rte_flow_action_vf.
+	 */
+	RTE_FLOW_ACTION_TYPE_VF,
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_MARK

lib/librte_ether/rte_flow.h line 658

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ *
+ * Attaches a 32 bit value to packets.
+ *
+ * This value is arbitrary and application-defined. For compatibility with
+ * FDIR it is returned in the hash.fdir.hi mbuf field. PKT_RX_FDIR_ID is
+ * also set in ol_flags.
+ */
+struct rte_flow_action_mark {
+	uint32_t id; /**< 32 bit value to return with packets. */
+};

One use case I thought we would be able to do for OVS is classification in hardware and the unique flow id is sent with the packet to software. But in OVS the ufid is 128 bits, so it means we can't and there is still the miniflow extract overhead. I'm not sure if there is a practical way around this.

Sugesh (cc'd) has looked at this before and may be able to comment or correct me.

Quoted 48 lines
lib/librte_ether/rte_flow.h new file +48 -0
+
+/**
+ * RTE_FLOW_ACTION_TYPE_QUEUE
+ *
+ * Assign packets to a given queue index.
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_queue {
+	uint16_t index; /**< Queue index to use. */
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_COUNT (query)
+ *
+ * Query structure to retrieve and reset flow rule counters.
+ */
+struct rte_flow_query_count {
+	uint32_t reset:1; /**< Reset counters after query [in]. */
+	uint32_t hits_set:1; /**< hits field is set [out]. */
+	uint32_t bytes_set:1; /**< bytes field is set [out]. */
+	uint32_t reserved:29; /**< Reserved, must be zero [in, out]. */
+	uint64_t hits; /**< Number of hits for this rule [out]. */
+	uint64_t bytes; /**< Number of bytes through this rule [out]. */
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_DUP
+ *
+ * Duplicates packets to a given queue index.
+ *
+ * This is normally combined with QUEUE, however when used alone, it is
+ * actually similar to QUEUE + PASSTHRU.
+ *
+ * Non-terminating by default.
+ */
+struct rte_flow_action_dup {
+	uint16_t index; /**< Queue index to duplicate packets to. */
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets to
+ * spread them among several queues according to the provided parameters.
+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf field,
+ * however it conflicts with the MARK action as they share the same

lib/librte_ether/rte_flow.h line 716

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ * space. When both actions are specified, the RSS hash is discarded and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_rss {
+	const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+	uint16_t queues; /**< Number of entries in queue[]. */
+	uint16_t queue[]; /**< Queues indices to use. */

I'd try and avoid queue and queues - someone will say "huh?" when reading code. s/queues/num ?

lib/librte_ether/rte_flow.h line 728

Quoted 12 lines
lib/librte_ether/rte_flow.h new file +12 -0
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_VF
+ *
+ * Redirects packets to a virtual function (VF) of the current device.
+ *
+ * Packets matched by a VF pattern item can be redirected to their original
+ * VF ID instead of the specified one. This parameter may not be available
+ * and is not guaranteed to work properly if the VF part is matched by a
+ * prior flow rule or if packets are not addressed to a VF in the first
+ * place.

Not clear what you mean by "not guaranteed to work if...". Please return fail when this action is used if this is not going to work.

Quoted 106 lines
lib/librte_ether/rte_flow.h new file +106 -0
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_vf {
+	uint32_t original:1; /**< Use original VF ID if possible. */
+	uint32_t reserved:31; /**< Reserved, must be zero. */
+	uint32_t id; /**< VF ID to redirect packets to. */
+};
+
+/**
+ * Definition of a single action.
+ *
+ * A list of actions is terminated by a END action.
+ *
+ * For simple actions without a configuration structure, conf remains NULL.
+ */
+struct rte_flow_action {
+	enum rte_flow_action_type type; /**< Action type. */
+	const void *conf; /**< Pointer to action configuration structure. */
+};
+
+/**
+ * Opaque type returned after successfully creating a flow.
+ *
+ * This handle can be used to manage and query the related flow (e.g. to
+ * destroy it or retrieve counters).
+ */
+struct rte_flow;
+
+/**
+ * Verbose error types.
+ *
+ * Most of them provide the type of the object referenced by struct
+ * rte_flow_error.cause.
+ */
+enum rte_flow_error_type {
+	RTE_FLOW_ERROR_TYPE_NONE, /**< No error. */
+	RTE_FLOW_ERROR_TYPE_UNSPECIFIED, /**< Cause unspecified. */
+	RTE_FLOW_ERROR_TYPE_HANDLE, /**< Flow rule (handle). */
+	RTE_FLOW_ERROR_TYPE_ATTR_GROUP, /**< Group field. */
+	RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, /**< Priority field. */
+	RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, /**< Ingress field. */
+	RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, /**< Egress field. */
+	RTE_FLOW_ERROR_TYPE_ATTR, /**< Attributes structure. */
+	RTE_FLOW_ERROR_TYPE_ITEM_NUM, /**< Pattern length. */
+	RTE_FLOW_ERROR_TYPE_ITEM, /**< Specific pattern item. */
+	RTE_FLOW_ERROR_TYPE_ACTION_NUM, /**< Number of actions. */
+	RTE_FLOW_ERROR_TYPE_ACTION, /**< Specific action. */
+};
+
+/**
+ * Verbose error structure definition.
+ *
+ * This object is normally allocated by applications and set by PMDs, the
+ * message points to a constant string which does not need to be freed by
+ * the application, however its pointer can be considered valid only as long
+ * as its associated DPDK port remains configured. Closing the underlying
+ * device or unloading the PMD invalidates it.
+ *
+ * Both cause and message may be NULL regardless of the error type.
+ */
+struct rte_flow_error {
+	enum rte_flow_error_type type; /**< Cause field and error types. */
+	const void *cause; /**< Object responsible for the error. */
+	const char *message; /**< Human-readable error message. */
+};
+
+/**
+ * Check whether a flow rule can be created on a given port.
+ *
+ * While this function has no effect on the target device, the flow rule is
+ * validated against its current configuration state and the returned value
+ * should be considered valid by the caller for that state only.
+ *
+ * The returned value is guaranteed to remain valid only as long as no
+ * successful calls to rte_flow_create() or rte_flow_destroy() are made in
+ * the meantime and no device parameter affecting flow rules in any way are
+ * modified, due to possible collisions or resource limitations (although in
+ * such cases EINVAL should not be returned).
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 if flow rule is valid and can be created. A negative errno value
+ *   otherwise (rte_errno is also set), the following errors are defined:
+ *
+ *   -ENOSYS: underlying device does not support this functionality.
+ *
+ *   -EINVAL: unknown or invalid rule specification.
+ *
+ *   -ENOTSUP: valid but unsupported rule specification (e.g. partial
+ *   bit-masks are unsupported).
+ *
+ *   -EEXIST: collision with an existing rule.
+ *
+ *   -ENOMEM: not enough resources.
+ *

lib/librte_ether/rte_flow.h line 844

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ *   -EBUSY: action cannot be performed due to busy device resources, may
+ *   succeed if the affected queues or even the entire port are in a stopped
+ *   state (see rte_eth_dev_rx_queue_stop() and rte_eth_dev_stop()).
+ */
+int
+rte_flow_validate(uint8_t port_id,
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error);

Why not just use rte_flow_create() and get an error? Is it less disruptive to do a validate and find the rule cannot be created, than using a create directly?

Quoted 16 lines
lib/librte_ether/rte_flow.h new file +16 -0
+
+/**
+ * Create a flow rule on a given port.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return

lib/librte_ether/rte_flow.h line 870

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ *   A valid handle in case of success, NULL otherwise and rte_errno is set
+ *   to the positive version of one of the error codes defined for
+ *   rte_flow_validate().
+ */
+struct rte_flow *
+rte_flow_create(uint8_t port_id,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error);

General question - are these functions threadsafe? In the OVS example you could have several threads wanting to create flow rules at the same time for same or different ports.

lib/librte_ether/rte_flow.h line 879

Quoted 9 lines
lib/librte_ether/rte_flow.h new file +9 -0
+
+/**
+ * Destroy a flow rule on a given port.
+ *
+ * Failure to destroy a flow rule handle may occur when other flow rules
+ * depend on it, and destroying it would result in an inconsistent state.
+ *
+ * This function is only guaranteed to succeed if handles are destroyed in
+ * reverse order of their creation.

How can the application find this information out on error?

Quoted 24 lines
lib/librte_ether/rte_flow.h new file +24 -0
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param flow
+ *   Flow rule handle to destroy.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+rte_flow_destroy(uint8_t port_id,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *error);
+
+/**
+ * Destroy all flow rules associated with a port.
+ *
+ * In the unlikely event of failure, handles are still considered destroyed
+ * and no longer valid but the port must be assumed to be in an inconsistent
+ * state.
+ *
+ * @param port_id

lib/librte_ether/rte_flow.h line 913

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+ *   Port identifier of Ethernet device.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+rte_flow_flush(uint8_t port_id,
+	       struct rte_flow_error *error);

rte_flow_destroy_all() would be more descriptive (but breaks your style)

lib/librte_ether/rte_flow.h line 920

Quoted 7 lines
lib/librte_ether/rte_flow.h new file +7 -0
+
+/**
+ * Query an existing flow rule.
+ *
+ * This function allows retrieving flow-specific data such as counters.
+ * Data is gathered by special actions which must be present in the flow
+ * rule definition.

re last sentence, it would be good if you can put a link to RTE_FLOW_ACTION_TYPE_COUNT

lib/librte_ether/rte_flow.h line 929

Quoted 9 lines
lib/librte_ether/rte_flow.h new file +9 -0
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param flow
+ *   Flow rule handle to query.
+ * @param action
+ *   Action type to query.
+ * @param[in, out] data
+ *   Pointer to storage for the associated query data type.

can this be anything other than rte_flow_query_count?

Quoted 6 lines
lib/librte_ether/rte_flow.h new file +6 -0
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.h line 945

Quoted 10 lines
lib/librte_ether/rte_flow.h new file +10 -0
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       struct rte_flow_error *error);
+
+#ifdef __cplusplus
+}
+#endif

I don't see a way to dump all the rules for a port out. I think this is neccessary for degbugging. You could have a look through dpif.h in OVS and see how dpif_flow_dump_next() is used, it might be a good reference.

Also, it would be nice if there were an api that would allow a test packet to be injected and traced for debugging - although I'm not exactly sure how well it could be traced. For reference:
http://developers.redhat.com/blog/2016/10/12/tracing-packets-inside-open-vswitch/

thanks,
Kevin.

Quoted 186 lines
lib/librte_ether/rte_flow.h new file +2 -0
+
+#endif /* RTE_FLOW_H_ */
lib/librte_ether/rte_flow_driver.h new file +177 -0
new file mode 100644
index 0000000..a88c621
@@ -0,0 +1,177 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2016 6WIND S.A.
+ *   Copyright 2016 Mellanox.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of 6WIND S.A. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RTE_FLOW_DRIVER_H_
+#define RTE_FLOW_DRIVER_H_
+
+/**
+ * @file
+ * RTE generic flow API (driver side)
+ *
+ * This file provides implementation helpers for internal use by PMDs, they
+ * are not intended to be exposed to applications and are not subject to ABI
+ * versioning.
+ */
+
+#include <stdint.h>
+
+#include <rte_errno.h>
+#include "rte_flow.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Generic flow operations structure implemented and returned by PMDs.
+ *
+ * To implement this API, PMDs must handle the RTE_ETH_FILTER_GENERIC filter
+ * type in their .filter_ctrl callback function (struct eth_dev_ops) as well
+ * as the RTE_ETH_FILTER_GET filter operation.
+ *
+ * If successful, this operation must result in a pointer to a PMD-specific
+ * struct rte_flow_ops written to the argument address as described below:
+ *
+ *  // PMD filter_ctrl callback
+ *
+ *  static const struct rte_flow_ops pmd_flow_ops = { ... };
+ *
+ *  switch (filter_type) {
+ *  case RTE_ETH_FILTER_GENERIC:
+ *      if (filter_op != RTE_ETH_FILTER_GET)
+ *          return -EINVAL;
+ *      *(const void **)arg = &pmd_flow_ops;
+ *      return 0;
+ *  }
+ *
+ * See also rte_flow_ops_get().
+ *
+ * These callback functions are not supposed to be used by applications
+ * directly, which must rely on the API defined in rte_flow.h.
+ *
+ * Public-facing wrapper functions perform a few consistency checks so that
+ * unimplemented (i.e. NULL) callbacks simply return -ENOTSUP. These
+ * callbacks otherwise only differ by their first argument (with port ID
+ * already resolved to a pointer to struct rte_eth_dev).
+ */
+struct rte_flow_ops {
+	/** See rte_flow_validate(). */
+	int (*validate)
+		(struct rte_eth_dev *,
+		 const struct rte_flow_attr *,
+		 const struct rte_flow_item [],
+		 const struct rte_flow_action [],
+		 struct rte_flow_error *);
+	/** See rte_flow_create(). */
+	struct rte_flow *(*create)
+		(struct rte_eth_dev *,
+		 const struct rte_flow_attr *,
+		 const struct rte_flow_item [],
+		 const struct rte_flow_action [],
+		 struct rte_flow_error *);
+	/** See rte_flow_destroy(). */
+	int (*destroy)
+		(struct rte_eth_dev *,
+		 struct rte_flow *,
+		 struct rte_flow_error *);
+	/** See rte_flow_flush(). */
+	int (*flush)
+		(struct rte_eth_dev *,
+		 struct rte_flow_error *);
+	/** See rte_flow_query(). */
+	int (*query)
+		(struct rte_eth_dev *,
+		 struct rte_flow *,
+		 enum rte_flow_action_type,
+		 void *,
+		 struct rte_flow_error *);
+};
+
+/**
+ * Initialize generic flow error structure.
+ *
+ * This function also sets rte_errno to a given value.
+ *
+ * @param[out] error
+ *   Pointer to flow error structure (may be NULL).
+ * @param code
+ *   Related error code (rte_errno).
+ * @param type
+ *   Cause field and error types.
+ * @param cause
+ *   Object responsible for the error.
+ * @param message
+ *   Human-readable error message.
+ *
+ * @return
+ *   Pointer to flow error structure.
+ */
+static inline struct rte_flow_error *
+rte_flow_error_set(struct rte_flow_error *error,
+		   int code,
+		   enum rte_flow_error_type type,
+		   void *cause,
+		   const char *message)
+{
+	if (error) {
+		*error = (struct rte_flow_error){
+			.type = type,
+			.cause = cause,
+			.message = message,
+		};
+	}
+	rte_errno = code;
+	return error;
+}
+
+/**
+ * Get generic flow operations structure from a port.
+ *
+ * @param port_id
+ *   Port identifier to query.
+ * @param[out] error
+ *   Pointer to flow error structure.
+ *
+ * @return
+ *   The flow operations structure associated with port_id, NULL in case of
+ *   error, in which case rte_errno is set and the error structure contains
+ *   additional details.
+ */
+const struct rte_flow_ops *
+rte_flow_ops_get(uint8_t port_id, struct rte_flow_error *error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTE_FLOW_DRIVER_H_ */
#67 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Kevin,

Quoted 16 lines · Kevin Traynor

Hi Adrien,

Quoted 13 lines · Adrien Mazarguil

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

Quoted 8 lines
Quoted 5 lines

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

I'd suggest to add a deprecation notice to deprecation.rst, ideally with a target release.

Will do, not a sure about the target release though. It seems a bit early since no PMD really supports this API yet.

[...]

Quoted 6 lines
Quoted 6 lines
lib/librte_ether/rte_flow.c new file +0 -0
new file mode 100644
index 0000000..064963d
@@ -0,0 +1,159 @@
Quoted 8 lines

lib/librte_ether/rte_flow.c line 5

Quoted 5 lines
lib/librte_ether/rte_flow.c new file +5 -0
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2016 6WIND S.A.
+ *   Copyright 2016 Mellanox.

There's Mellanox copyright but you are the only signed-off-by - is that right?

Yes, I'm the primary maintainer for Mellanox PMDs and this API was designed on their behalf to expose several features from mlx4/mlx5 as the existing filter types had too many limitations.

[...]

Quoted 19 lines
Quoted 19 lines
lib/librte_ether/rte_flow.c new file +19 -0
+/* Get generic flow operations structure from a port. */
+const struct rte_flow_ops *
+rte_flow_ops_get(uint8_t port_id, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops;
+	int code;
+
+	if (unlikely(!rte_eth_dev_is_valid_port(port_id)))
+		code = ENODEV;
+	else if (unlikely(!dev->dev_ops->filter_ctrl ||
+			  dev->dev_ops->filter_ctrl(dev,
+						    RTE_ETH_FILTER_GENERIC,
+						    RTE_ETH_FILTER_GET,
+						    &ops) ||
+			  !ops))
+		code = ENOTSUP;
+	else
+		return ops;
Quoted 8 lines

lib/librte_ether/rte_flow.c line 65

Quoted 5 lines
lib/librte_ether/rte_flow.c new file +5 -0
+	rte_flow_error_set(error, code, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(code));
+	return NULL;
+}
+

Is it expected that the application or pmd will provide locking between these functions if required? I think it's going to have to be the app.

Locking is indeed expected to be performed by applications. This API only documents places where locking would make sense if necessary and expected behavior.

Like all control path APIs, this one assumes a single control thread. Applications must take the necessary precautions.

[...]

Quoted 22 lines
Quoted 11 lines
lib/librte_ether/rte_flow.c new file +11 -0
+/**
+ * Flow rule attributes.
+ *
+ * Priorities are set on two levels: per group and per rule within groups.
+ *
+ * Lower values denote higher priority, the highest priority for both levels
+ * is 0, so that a rule with priority 0 in group 8 is always matched after a
+ * rule with priority 8 in group 0.
+ *
+ * Although optional, applications are encouraged to group similar rules as
+ * much as possible to fully take advantage of hardware capabilities

lib/librte_ether/rte_flow.c line 50

Quoted 10 lines
lib/librte_ether/rte_flow.c new file +10 -0
+ * (e.g. optimized matching) and work around limitations (e.g. a single
+ * pattern type possibly allowed in a given group).
+ *
+ * Group and priority levels are arbitrary and up to the application, they
+ * do not need to be contiguous nor start from 0, however the maximum number
+ * varies between devices and may be affected by existing flow rules.
+ *
+ * If a packet is matched by several rules of a given group for a given
+ * priority level, the outcome is undefined. It can take any path, may be
+ * duplicated or even cause unrecoverable errors.
Quoted 6 lines

I get what you are trying to do here wrt supporting multiple pmds/hardware implementations and it's a good idea to keep it flexible.

Given that the outcome is undefined, it would be nice that the application has a way of finding the specific effects for verification and debugging.

Right, however it was deemed a bit difficult to manage in many cases hence the vagueness.

For example, suppose two rules with the same group and priority, one matching any IPv4 header, the other one any UDP header:

- TCPv4 packets => rule #1.
- UDPv6 packets => rule #2.
- UDPv4 packets => both?

That last one is perhaps invalid, checking that some unspecified protocol combination does not overlap is expensive and may miss corner cases, even assuming this is not an issue, what if the application guarantees that no UDPv4 packets can ever hit that rule?

Suggestions are welcome though, perhaps we can refine the description.

Quoted 21 lines
Quoted 21 lines
lib/librte_ether/rte_flow.c new file +21 -0
+ *
+ * Note that support for more than a single group and priority level is not
+ * guaranteed.
+ *
+ * Flow rules can apply to inbound and/or outbound traffic (ingress/egress).
+ *
+ * Several pattern items and actions are valid and can be used in both
+ * directions. Those valid for only one direction are described as such.
+ *
+ * Specifying both directions at once is not recommended but may be valid in
+ * some cases, such as incrementing the same counter twice.
+ *
+ * Not specifying any direction is currently an error.
+ */
+struct rte_flow_attr {
+	uint32_t group; /**< Priority group. */
+	uint32_t priority; /**< Priority level within group. */
+	uint32_t ingress:1; /**< Rule applies to ingress traffic. */
+	uint32_t egress:1; /**< Rule applies to egress traffic. */
+	uint32_t reserved:30; /**< Reserved, must be zero. */
+};

[...]

Quoted 18 lines
Quoted 9 lines

+/**
+ * RTE_FLOW_ITEM_TYPE_VF
+ *
+ * Matches packets addressed to a virtual function ID of the device.
+ *
+ * If the underlying device function differs from the one that would
+ * normally receive the matched traffic, specifying this item prevents it
+ * from reaching that device unless the flow rule contains a VF
+ * action. Packets are not duplicated between device instances by default.

Quoted 8 lines

+ *
+ * - Likely to return an error or never match any traffic if this causes a
+ * VF device to match traffic addressed to a different VF.
+ * - Can be specified multiple times to match traffic addressed to several
+ * specific VFs.
+ * - Can be combined with a PF item to match both PF and VF traffic.
+ *
+ * A zeroed mask can be used to match any VF.

Quoted 1 line

can you refer explicitly to id

If you mean "VF" to "VF ID" then yes, will do it for v2.

Quoted 4 lines
Quoted 4 lines

+ */

lib/librte_ether/rte_flow.c new file +3 -0
+struct rte_flow_item_vf {
+	uint32_t id; /**< Destination VF ID. */
+};

[...]

Quoted 20 lines
Quoted 11 lines

+/**
+ * Matching pattern item definition.
+ *
+ * A pattern is formed by stacking items starting from the lowest protocol
+ * layer to match. This stacking restriction does not apply to meta items
+ * which can be placed anywhere in the stack with no effect on the meaning
+ * of the resulting pattern.
+ *
+ * A stack is terminated by a END item.
+ *
+ * The spec field should be a valid pointer to a structure of the related

Quoted 8 lines

+ * item type. It may be set to NULL in many cases to use default values.
+ *
+ * Optionally, last can point to a structure of the same type to define an
+ * inclusive range. This is mostly supported by integer and address fields,
+ * may cause errors otherwise. Fields that do not support ranges must be set
+ * to the same value as their spec counterparts.
+ *
+ * By default all fields present in spec are considered relevant.* This

Quoted 1 line

typo "*"

No, that's an asterisk for a footnote below. Perhaps it is a bit unusual, would something like "[1]" look better?

Quoted 11 lines
Quoted 11 lines

+ * behavior can be altered by providing a mask structure of the same type
+ * with applicable bits set to one. It can also be used to partially filter
+ * out specific fields (e.g. as an alternate mean to match ranges of IP
+ * addresses).
+ *
+ * Note this is a simple bit-mask applied before interpreting the contents
+ * of spec and last, which may yield unexpected results if not used
+ * carefully. For example, if for an IPv4 address field, spec provides
+ * 10.1.2.3, last provides 10.3.4.5 and mask provides 255.255.0.0, the
+ * effective range is 10.1.0.0 to 10.3.255.255.
+ *

See footnote below:

Quoted 32 lines
Quoted 32 lines

+ * * The defaults for data-matching items such as IPv4 when mask is not
+ * specified actually depend on the underlying implementation since only
+ * recognized fields can be taken into account.
+ */

lib/librte_ether/rte_flow.c new file +28 -0
+struct rte_flow_item {
+	enum rte_flow_item_type type; /**< Item type. */
+	const void *spec; /**< Pointer to item specification structure. */
+	const void *last; /**< Defines an inclusive range (spec to last). */
+	const void *mask; /**< Bit-mask applied to spec and last. */
+};
+
+/**
+ * Action types.
+ *
+ * Each possible action is represented by a type. Some have associated
+ * configuration structures. Several actions combined in a list can be
+ * affected to a flow rule. That list is not ordered.
+ *
+ * They fall in three categories:
+ *
+ * - Terminating actions (such as QUEUE, DROP, RSS, PF, VF) that prevent
+ *   processing matched packets by subsequent flow rules, unless overridden
+ *   with PASSTHRU.
+ *
+ * - Non terminating actions (PASSTHRU, DUP) that leave matched packets up
+ *   for additional processing by subsequent flow rules.
+ *
+ * - Other non terminating meta actions that do not affect the fate of
+ *   packets (END, VOID, MARK, FLAG, COUNT).
+ *
+ * When several actions are combined in a flow rule, they should all have
+ * different types (e.g. dropping a packet twice is not possible). The
Quoted 8 lines
Quoted 3 lines

+ * defined behavior is for PMDs to only take into account the last action of
+ * a given type found in the list. PMDs still perform error checking on the
+ * entire list.

why do you define that the pmd will interpret multiple same type rules in this way...would it not make more sense for the pmd to just return EINVAL for an invalid set of rules? It seems more transparent for the application.

Well, I had to define something as a default. The reason is that any number of VOID actions may specified and did not want that to be a special case in order to keep PMD parsers as simple as possible. I'll settle for EINVAL (or some other error) if at least one PMD maintainer other than Nelio who intends to implement this API is not convinced by this explanation, all right?

[...]

Quoted 19 lines

lib/librte_ether/rte_flow.c

Quoted 12 lines
lib/librte_ether/rte_flow.c new file +12 -0
+/**
+ * RTE_FLOW_ACTION_TYPE_MARK
+ *
+ * Attaches a 32 bit value to packets.
+ *
+ * This value is arbitrary and application-defined. For compatibility with
+ * FDIR it is returned in the hash.fdir.hi mbuf field. PKT_RX_FDIR_ID is
+ * also set in ol_flags.
+ */
+struct rte_flow_action_mark {
+	uint32_t id; /**< 32 bit value to return with packets. */
+};

One use case I thought we would be able to do for OVS is classification in hardware and the unique flow id is sent with the packet to software. But in OVS the ufid is 128 bits, so it means we can't and there is still the miniflow extract overhead. I'm not sure if there is a practical way around this.

Quoted 2 lines

Sugesh (cc'd) has looked at this before and may be able to comment or correct me.

Yes, we settled on 32 bit because currently no known hardware implementation supports more than this. If that changes, another action with a larger type shall be provided (no ABI breakage).

Also since even 64 bit would not be enough for the use case you mention, there is no choice but use this as an indirect value (such as an array or hash table index/value).

[...]

Quoted 13 lines
Quoted 13 lines

+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets to
+ * spread them among several queues according to the provided parameters.
+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf field,
+ * however it conflicts with the MARK action as they share the same
+ * space. When both actions are specified, the RSS hash is discarded and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.

Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 5 lines

+ */

lib/librte_ether/rte_flow.c new file +4 -0
+struct rte_flow_action_rss {
+	const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+	uint16_t queues; /**< Number of entries in queue[]. */
+	uint16_t queue[]; /**< Queues indices to use. */

I'd try and avoid queue and queues - someone will say "huh?" when reading code. s/queues/num ?

Agreed, will update for v2.

Quoted 7 lines
Quoted 7 lines
lib/librte_ether/rte_flow.c new file +7 -0
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_VF
+ *
+ * Redirects packets to a virtual function (VF) of the current device.
+ *
Quoted 8 lines
Quoted 5 lines

+ * Packets matched by a VF pattern item can be redirected to their original
+ * VF ID instead of the specified one. This parameter may not be available
+ * and is not guaranteed to work properly if the VF part is matched by a
+ * prior flow rule or if packets are not addressed to a VF in the first
+ * place.

Not clear what you mean by "not guaranteed to work if...". Please return fail when this action is used if this is not going to work.

Again, this is a case where it is difficult for a PMD to determine if the entire list of flow rules makes sense. Perhaps it does, perhaps whatever goes through has already been filtered out of possible issues.

Here the documentation states the precautions an application should take to guarantee it will work as intended. Perhaps it can be reworded (any suggestion?), but a PMD can certainly not provide any strong guarantee.

Quoted 8 lines
Quoted 8 lines

+ *
+ * Terminating by default.
+ */

lib/librte_ether/rte_flow.c new file +5 -0
+struct rte_flow_action_vf {
+	uint32_t original:1; /**< Use original VF ID if possible. */
+	uint32_t reserved:31; /**< Reserved, must be zero. */
+	uint32_t id; /**< VF ID to redirect packets to. */
+};

[...]

Quoted 45 lines
Quoted 45 lines
lib/librte_ether/rte_flow.c new file +45 -0
+/**
+ * Check whether a flow rule can be created on a given port.
+ *
+ * While this function has no effect on the target device, the flow rule is
+ * validated against its current configuration state and the returned value
+ * should be considered valid by the caller for that state only.
+ *
+ * The returned value is guaranteed to remain valid only as long as no
+ * successful calls to rte_flow_create() or rte_flow_destroy() are made in
+ * the meantime and no device parameter affecting flow rules in any way are
+ * modified, due to possible collisions or resource limitations (although in
+ * such cases EINVAL should not be returned).
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 if flow rule is valid and can be created. A negative errno value
+ *   otherwise (rte_errno is also set), the following errors are defined:
+ *
+ *   -ENOSYS: underlying device does not support this functionality.
+ *
+ *   -EINVAL: unknown or invalid rule specification.
+ *
+ *   -ENOTSUP: valid but unsupported rule specification (e.g. partial
+ *   bit-masks are unsupported).
+ *
+ *   -EEXIST: collision with an existing rule.
+ *
+ *   -ENOMEM: not enough resources.
+ *
+ *   -EBUSY: action cannot be performed due to busy device resources, may
+ *   succeed if the affected queues or even the entire port are in a stopped
+ *   state (see rte_eth_dev_rx_queue_stop() and rte_eth_dev_stop()).
+ */
+int
+rte_flow_validate(uint8_t port_id,
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error);

Why not just use rte_flow_create() and get an error? Is it less disruptive to do a validate and find the rule cannot be created, than using a create directly?

The rationale can be found in the original RFC, which I'll convert to actual documentation in v2. In short:

- Calling rte_flow_validate() before rte_flow_create() is useless since rte_flow_create() also performs validation.

- We cannot possibly express a full static set of allowed flow rules, even if we could, it usually depends on the current hardware configuration therefore would not be static.

- rte_flow_validate() is thus provided as a replacement for capability flags. It can be used to determine during initialization if the underlying device can support the typical flow rules an application might want to provide later and do something useful with that information (e.g. always use software fallback due to HW limitations).

- rte_flow_validate() being a subset of rte_flow_create(), it is essentially free to expose.

Quoted 22 lines
Quoted 22 lines
lib/librte_ether/rte_flow.c new file +22 -0
+
+/**
+ * Create a flow rule on a given port.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   A valid handle in case of success, NULL otherwise and rte_errno is set
+ *   to the positive version of one of the error codes defined for
+ *   rte_flow_validate().
+ */
+struct rte_flow *
+rte_flow_create(uint8_t port_id,
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error);

General question - are these functions threadsafe? In the OVS example you could have several threads wanting to create flow rules at the same time for same or different ports.

No they aren't, applications have to perform their own locking. The RFC (to be converted to actual documentation in v2) says that:

- API operations are synchronous and blocking (``EAGAIN`` cannot be returned).

- There is no provision for reentrancy/multi-thread safety, although nothing should prevent different devices from being configured at the same time. PMDs may protect their control path functions accordingly.

Quoted 11 lines

lib/librte_ether/rte_flow.c

Quoted 9 lines
lib/librte_ether/rte_flow.c new file +9 -0
+
+/**
+ * Destroy a flow rule on a given port.
+ *
+ * Failure to destroy a flow rule handle may occur when other flow rules
+ * depend on it, and destroying it would result in an inconsistent state.
+ *
+ * This function is only guaranteed to succeed if handles are destroyed in
+ * reverse order of their creation.

How can the application find this information out on error?

Without maintaining a list, they cannot. The specified case is the only possible guarantee. That does not mean PMDs should not do their best to destroy flow rules, only that ordering must remain consistent in case of inability to destroy one.

What do you suggest?

Quoted 28 lines
Quoted 28 lines

+ *
+ * @param port_id
+ * Port identifier of Ethernet device.
+ * @param flow
+ * Flow rule handle to destroy.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +17 -0
+int
+rte_flow_destroy(uint8_t port_id,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *error);
+
+/**
+ * Destroy all flow rules associated with a port.
+ *
+ * In the unlikely event of failure, handles are still considered destroyed
+ * and no longer valid but the port must be assumed to be in an inconsistent
+ * state.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 6 lines

+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +3 -0
+int
+rte_flow_flush(uint8_t port_id,
+	       struct rte_flow_error *error);

rte_flow_destroy_all() would be more descriptive (but breaks your style)

There are enough underscores as it is. I like flush, if enough people complain we'll change it but it has to occur before the first public release.

Quoted 10 lines

lib/librte_ether/rte_flow.c

Quoted 7 lines
lib/librte_ether/rte_flow.c new file +7 -0
+
+/**
+ * Query an existing flow rule.
+ *
+ * This function allows retrieving flow-specific data such as counters.
+ * Data is gathered by special actions which must be present in the flow
+ * rule definition.

re last sentence, it would be good if you can put a link to RTE_FLOW_ACTION_TYPE_COUNT

Will do, I did not know how until very recently.

Quoted 11 lines
Quoted 9 lines

+ *
+ * @param port_id
+ * Port identifier of Ethernet device.
+ * @param flow
+ * Flow rule handle to query.
+ * @param action
+ * Action type to query.
+ * @param[in, out] data
+ * Pointer to storage for the associated query data type.

can this be anything other than rte_flow_query_count?

Likely in the future. I've only defined this one as a counterpart for existing API functionality and because we wanted to expose it in mlx5.

Quoted 12 lines
Quoted 12 lines

+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +6 -0
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       struct rte_flow_error *error);
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+
+#ifdef __cplusplus
+}
+#endif

I don't see a way to dump all the rules for a port out. I think this is neccessary for degbugging. You could have a look through dpif.h in OVS and see how dpif_flow_dump_next() is used, it might be a good reference.

DPDK does not maintain flow rules and, depending on hardware capabilities and level of compliance, PMDs do not necessarily do it either, particularly since it requires space and application probably have a better method to store these pointers for their own needs.

What you see here is only a PMD interface. Depending on applications needs, generic helper functions built on top of these may be added to manage flow rules in the future.

Quoted 4 lines

Also, it would be nice if there were an api that would allow a test packet to be injected and traced for debugging - although I'm not exactly sure how well it could be traced. For reference:
http://developers.redhat.com/blog/2016/10/12/tracing-packets-inside-open-vswitch/

Thanks for the link, I'm not sure how you'd do this either. Remember, as generic as it looks, this interface is only meant to configure the underlying device. You need to see it as one big offload, everything else is left to applications.

Signature · 2 lines
Adrien Mazarguil
6WIND
#68 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Quoted 80 lines · Adrien Mazarguil

Hi Kevin,

Quoted 16 lines · Kevin Traynor

Hi Adrien,

Quoted 13 lines · Adrien Mazarguil

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

Quoted 8 lines
Quoted 5 lines

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

I'd suggest to add a deprecation notice to deprecation.rst, ideally with a target release.

Will do, not a sure about the target release though. It seems a bit early since no PMD really supports this API yet.

[...]

Quoted 6 lines
Quoted 6 lines
lib/librte_ether/rte_flow.c new file +0 -0
new file mode 100644
index 0000000..064963d
@@ -0,0 +1,159 @@
Quoted 8 lines

lib/librte_ether/rte_flow.c line 5

Quoted 5 lines
lib/librte_ether/rte_flow.c new file +5 -0
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2016 6WIND S.A.
+ *   Copyright 2016 Mellanox.

There's Mellanox copyright but you are the only signed-off-by - is that right?

Yes, I'm the primary maintainer for Mellanox PMDs and this API was designed on their behalf to expose several features from mlx4/mlx5 as the existing filter types had too many limitations.

[...]

Quoted 19 lines
Quoted 19 lines
lib/librte_ether/rte_flow.c new file +19 -0
+/* Get generic flow operations structure from a port. */
+const struct rte_flow_ops *
+rte_flow_ops_get(uint8_t port_id, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops;
+	int code;
+
+	if (unlikely(!rte_eth_dev_is_valid_port(port_id)))
+		code = ENODEV;
+	else if (unlikely(!dev->dev_ops->filter_ctrl ||
+			  dev->dev_ops->filter_ctrl(dev,
+						    RTE_ETH_FILTER_GENERIC,
+						    RTE_ETH_FILTER_GET,
+						    &ops) ||
+			  !ops))
+		code = ENOTSUP;
+	else
+		return ops;
Quoted 8 lines

lib/librte_ether/rte_flow.c line 65

Quoted 5 lines
lib/librte_ether/rte_flow.c new file +5 -0
+	rte_flow_error_set(error, code, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(code));
+	return NULL;
+}
+

Is it expected that the application or pmd will provide locking between these functions if required? I think it's going to have to be the app.

Quoted 6 lines

Locking is indeed expected to be performed by applications. This API only documents places where locking would make sense if necessary and expected behavior.

Like all control path APIs, this one assumes a single control thread. Applications must take the necessary precautions.

If you look at OVS now it's quite possible that you have 2 rx queues serviced by different threads, that would also install the flow rules in the software flow caches - possibly that could extend to adding hardware flows. There could also be another thread that is querying for stats. So anything that can be done to minimise the locking would be helpful - maybe query() could be atomic and not require any locking?

Quoted 37 lines

[...]

Quoted 22 lines
Quoted 13 lines

+/**
+ * Flow rule attributes.
+ *
+ * Priorities are set on two levels: per group and per rule within groups.
+ *
+ * Lower values denote higher priority, the highest priority for both levels
+ * is 0, so that a rule with priority 0 in group 8 is always matched after a
+ * rule with priority 8 in group 0.
+ *
+ * Although optional, applications are encouraged to group similar rules as
+ * much as possible to fully take advantage of hardware capabilities
+ * (e.g. optimized matching) and work around limitations (e.g. a single
+ * pattern type possibly allowed in a given group).

Quoted 8 lines

+ *
+ * Group and priority levels are arbitrary and up to the application, they
+ * do not need to be contiguous nor start from 0, however the maximum number
+ * varies between devices and may be affected by existing flow rules.
+ *
+ * If a packet is matched by several rules of a given group for a given
+ * priority level, the outcome is undefined. It can take any path, may be
+ * duplicated or even cause unrecoverable errors.

Quoted 6 lines

I get what you are trying to do here wrt supporting multiple pmds/hardware implementations and it's a good idea to keep it flexible.

Given that the outcome is undefined, it would be nice that the application has a way of finding the specific effects for verification and debugging.

Right, however it was deemed a bit difficult to manage in many cases hence the vagueness.

For example, suppose two rules with the same group and priority, one matching any IPv4 header, the other one any UDP header:

Quoted 8 lines

- TCPv4 packets => rule #1.
- UDPv6 packets => rule #2.
- UDPv4 packets => both?

That last one is perhaps invalid, checking that some unspecified protocol combination does not overlap is expensive and may miss corner cases, even assuming this is not an issue, what if the application guarantees that no UDPv4 packets can ever hit that rule?

that's fine - I don't expect the software to be able to know what the hardware will do with those rules. It's more about trying to get a dump from the hardware if something goes wrong. Anyway covered in comment later.

Quoted 72 lines

Suggestions are welcome though, perhaps we can refine the description

Quoted 21 lines
Quoted 21 lines

+ *
+ * Note that support for more than a single group and priority level is not
+ * guaranteed.
+ *
+ * Flow rules can apply to inbound and/or outbound traffic (ingress/egress).
+ *
+ * Several pattern items and actions are valid and can be used in both
+ * directions. Those valid for only one direction are described as such.
+ *
+ * Specifying both directions at once is not recommended but may be valid in
+ * some cases, such as incrementing the same counter twice.
+ *
+ * Not specifying any direction is currently an error.
+ */

lib/librte_ether/rte_flow.c new file +7 -0
+struct rte_flow_attr {
+	uint32_t group; /**< Priority group. */
+	uint32_t priority; /**< Priority level within group. */
+	uint32_t ingress:1; /**< Rule applies to ingress traffic. */
+	uint32_t egress:1; /**< Rule applies to egress traffic. */
+	uint32_t reserved:30; /**< Reserved, must be zero. */
+};

[...]

Quoted 18 lines
Quoted 9 lines

+/**
+ * RTE_FLOW_ITEM_TYPE_VF
+ *
+ * Matches packets addressed to a virtual function ID of the device.
+ *
+ * If the underlying device function differs from the one that would
+ * normally receive the matched traffic, specifying this item prevents it
+ * from reaching that device unless the flow rule contains a VF
+ * action. Packets are not duplicated between device instances by default.

Quoted 8 lines

+ *
+ * - Likely to return an error or never match any traffic if this causes a
+ * VF device to match traffic addressed to a different VF.
+ * - Can be specified multiple times to match traffic addressed to several
+ * specific VFs.
+ * - Can be combined with a PF item to match both PF and VF traffic.
+ *
+ * A zeroed mask can be used to match any VF.

Quoted 1 line

can you refer explicitly to id

If you mean "VF" to "VF ID" then yes, will do it for v2.

Quoted 4 lines
Quoted 4 lines

+ */

lib/librte_ether/rte_flow.c new file +3 -0
+struct rte_flow_item_vf {
+	uint32_t id; /**< Destination VF ID. */
+};

[...]

Quoted 20 lines
Quoted 19 lines

+/**
+ * Matching pattern item definition.
+ *
+ * A pattern is formed by stacking items starting from the lowest protocol
+ * layer to match. This stacking restriction does not apply to meta items
+ * which can be placed anywhere in the stack with no effect on the meaning
+ * of the resulting pattern.
+ *
+ * A stack is terminated by a END item.
+ *
+ * The spec field should be a valid pointer to a structure of the related
+ * item type. It may be set to NULL in many cases to use default values.
+ *
+ * Optionally, last can point to a structure of the same type to define an
+ * inclusive range. This is mostly supported by integer and address fields,
+ * may cause errors otherwise. Fields that do not support ranges must be set
+ * to the same value as their spec counterparts.
+ *
+ * By default all fields present in spec are considered relevant.* This

Quoted 4 lines
Quoted 1 line

typo "*"

No, that's an asterisk for a footnote below. Perhaps it is a bit unusual, would something like "[1]" look better?

oh, I thought it was the start of a comment line gone astray. Maybe "See note below", no big deal though.

Quoted 56 lines
Quoted 12 lines
Quoted 12 lines

+ * behavior can be altered by providing a mask structure of the same type
+ * with applicable bits set to one. It can also be used to partially filter
+ * out specific fields (e.g. as an alternate mean to match ranges of IP
+ * addresses).
+ *
+ * Note this is a simple bit-mask applied before interpreting the contents
+ * of spec and last, which may yield unexpected results if not used
+ * carefully. For example, if for an IPv4 address field, spec provides
+ * 10.1.2.3, last provides 10.3.4.5 and mask provides 255.255.0.0, the
+ * effective range is 10.1.0.0 to 10.3.255.255.
+ *

See footnote below:

Quoted 32 lines
Quoted 32 lines

+ * * The defaults for data-matching items such as IPv4 when mask is not
+ * specified actually depend on the underlying implementation since only
+ * recognized fields can be taken into account.
+ */

lib/librte_ether/rte_flow.c new file +28 -0
+struct rte_flow_item {
+	enum rte_flow_item_type type; /**< Item type. */
+	const void *spec; /**< Pointer to item specification structure. */
+	const void *last; /**< Defines an inclusive range (spec to last). */
+	const void *mask; /**< Bit-mask applied to spec and last. */
+};
+
+/**
+ * Action types.
+ *
+ * Each possible action is represented by a type. Some have associated
+ * configuration structures. Several actions combined in a list can be
+ * affected to a flow rule. That list is not ordered.
+ *
+ * They fall in three categories:
+ *
+ * - Terminating actions (such as QUEUE, DROP, RSS, PF, VF) that prevent
+ *   processing matched packets by subsequent flow rules, unless overridden
+ *   with PASSTHRU.
+ *
+ * - Non terminating actions (PASSTHRU, DUP) that leave matched packets up
+ *   for additional processing by subsequent flow rules.
+ *
+ * - Other non terminating meta actions that do not affect the fate of
+ *   packets (END, VOID, MARK, FLAG, COUNT).
+ *
+ * When several actions are combined in a flow rule, they should all have
+ * different types (e.g. dropping a packet twice is not possible). The
Quoted 8 lines
Quoted 3 lines

+ * defined behavior is for PMDs to only take into account the last action of
+ * a given type found in the list. PMDs still perform error checking on the
+ * entire list.

why do you define that the pmd will interpret multiple same type rules in this way...would it not make more sense for the pmd to just return EINVAL for an invalid set of rules? It seems more transparent for the application.

Quoted 8 lines

Well, I had to define something as a default. The reason is that any number of VOID actions may specified and did not want that to be a special case in order to keep PMD parsers as simple as possible. I'll settle for EINVAL (or some other error) if at least one PMD maintainer other than Nelio who intends to implement this API is not convinced by this explanation, all right?

From an API perspective I think it's cleaner to pass or fail with the

input rather than change it. But yes, please take pmd maintainers input as to what is reasonable to check also.

Quoted 24 lines

[...]

Quoted 19 lines

lib/librte_ether/rte_flow.c

Quoted 12 lines
lib/librte_ether/rte_flow.c new file +12 -0
+/**
+ * RTE_FLOW_ACTION_TYPE_MARK
+ *
+ * Attaches a 32 bit value to packets.
+ *
+ * This value is arbitrary and application-defined. For compatibility with
+ * FDIR it is returned in the hash.fdir.hi mbuf field. PKT_RX_FDIR_ID is
+ * also set in ol_flags.
+ */
+struct rte_flow_action_mark {
+	uint32_t id; /**< 32 bit value to return with packets. */
+};

One use case I thought we would be able to do for OVS is classification in hardware and the unique flow id is sent with the packet to software. But in OVS the ufid is 128 bits, so it means we can't and there is still the miniflow extract overhead. I'm not sure if there is a practical way around this.

Quoted 2 lines

Sugesh (cc'd) has looked at this before and may be able to comment or correct me.

Quoted 7 lines

Yes, we settled on 32 bit because currently no known hardware implementation supports more than this. If that changes, another action with a larger type shall be provided (no ABI breakage).

Also since even 64 bit would not be enough for the use case you mention, there is no choice but use this as an indirect value (such as an array or hash table index/value).

ok, cool. I think Sugesh has other ideas anyway!

Quoted 42 lines

[...]

Quoted 13 lines
Quoted 13 lines

+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets to
+ * spread them among several queues according to the provided parameters.
+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf field,
+ * however it conflicts with the MARK action as they share the same
+ * space. When both actions are specified, the RSS hash is discarded and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.

Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 5 lines

+ */

lib/librte_ether/rte_flow.c new file +4 -0
+struct rte_flow_action_rss {
+	const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+	uint16_t queues; /**< Number of entries in queue[]. */
+	uint16_t queue[]; /**< Queues indices to use. */

I'd try and avoid queue and queues - someone will say "huh?" when reading code. s/queues/num ?

Agreed, will update for v2.

Quoted 7 lines
Quoted 7 lines
lib/librte_ether/rte_flow.c new file +7 -0
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_VF
+ *
+ * Redirects packets to a virtual function (VF) of the current device.
+ *
Quoted 8 lines
Quoted 5 lines

+ * Packets matched by a VF pattern item can be redirected to their original
+ * VF ID instead of the specified one. This parameter may not be available
+ * and is not guaranteed to work properly if the VF part is matched by a
+ * prior flow rule or if packets are not addressed to a VF in the first
+ * place.

Not clear what you mean by "not guaranteed to work if...". Please return fail when this action is used if this is not going to work.

Quoted 7 lines

Again, this is a case where it is difficult for a PMD to determine if the entire list of flow rules makes sense. Perhaps it does, perhaps whatever goes through has already been filtered out of possible issues.

Here the documentation states the precautions an application should take to guarantee it will work as intended. Perhaps it can be reworded (any suggestion?), but a PMD can certainly not provide any strong guarantee.

I see your point. Maybe for easy check things the pmd would return fail, but for more complex I agree it's too difficult.

Quoted 74 lines
Quoted 9 lines
Quoted 9 lines

+ *
+ * Terminating by default.
+ */

lib/librte_ether/rte_flow.c new file +5 -0
+struct rte_flow_action_vf {
+	uint32_t original:1; /**< Use original VF ID if possible. */
+	uint32_t reserved:31; /**< Reserved, must be zero. */
+	uint32_t id; /**< VF ID to redirect packets to. */
+};

[...]

Quoted 45 lines
Quoted 45 lines
lib/librte_ether/rte_flow.c new file +45 -0
+/**
+ * Check whether a flow rule can be created on a given port.
+ *
+ * While this function has no effect on the target device, the flow rule is
+ * validated against its current configuration state and the returned value
+ * should be considered valid by the caller for that state only.
+ *
+ * The returned value is guaranteed to remain valid only as long as no
+ * successful calls to rte_flow_create() or rte_flow_destroy() are made in
+ * the meantime and no device parameter affecting flow rules in any way are
+ * modified, due to possible collisions or resource limitations (although in
+ * such cases EINVAL should not be returned).
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 if flow rule is valid and can be created. A negative errno value
+ *   otherwise (rte_errno is also set), the following errors are defined:
+ *
+ *   -ENOSYS: underlying device does not support this functionality.
+ *
+ *   -EINVAL: unknown or invalid rule specification.
+ *
+ *   -ENOTSUP: valid but unsupported rule specification (e.g. partial
+ *   bit-masks are unsupported).
+ *
+ *   -EEXIST: collision with an existing rule.
+ *
+ *   -ENOMEM: not enough resources.
+ *
+ *   -EBUSY: action cannot be performed due to busy device resources, may
+ *   succeed if the affected queues or even the entire port are in a stopped
+ *   state (see rte_eth_dev_rx_queue_stop() and rte_eth_dev_stop()).
+ */
+int
+rte_flow_validate(uint8_t port_id,
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error);

Why not just use rte_flow_create() and get an error? Is it less disruptive to do a validate and find the rule cannot be created, than using a create directly?

The rationale can be found in the original RFC, which I'll convert to actual documentation in v2. In short:

- Calling rte_flow_validate() before rte_flow_create() is useless since rte_flow_create() also performs validation.

- We cannot possibly express a full static set of allowed flow rules, even if we could, it usually depends on the current hardware configuration therefore would not be static.

Quoted 8 lines

- rte_flow_validate() is thus provided as a replacement for capability flags. It can be used to determine during initialization if the underlying device can support the typical flow rules an application might want to provide later and do something useful with that information (e.g. always use software fallback due to HW limitations).

- rte_flow_validate() being a subset of rte_flow_create(), it is essentially free to expose.

make sense now, thanks.

Quoted 35 lines
Quoted 23 lines
Quoted 23 lines
lib/librte_ether/rte_flow.c new file +22 -0
+
+/**
+ * Create a flow rule on a given port.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   A valid handle in case of success, NULL otherwise and rte_errno is set
+ *   to the positive version of one of the error codes defined for
+ *   rte_flow_validate().
+ */
+struct rte_flow *
+rte_flow_create(uint8_t port_id,
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error);

General question - are these functions threadsafe? In the OVS example you could have several threads wanting to create flow rules at the same time for same or different ports.

No they aren't, applications have to perform their own locking. The RFC (to be converted to actual documentation in v2) says that:

Quoted 6 lines

- API operations are synchronous and blocking (``EAGAIN`` cannot be returned).

- There is no provision for reentrancy/multi-thread safety, although nothing should prevent different devices from being configured at the same time. PMDs may protect their control path functions accordingly.

other comment above wrt locking.

Quoted 13 lines
Quoted 12 lines

lib/librte_ether/rte_flow.c

Quoted 10 lines
lib/librte_ether/rte_flow.c new file +9 -0
+
+/**
+ * Destroy a flow rule on a given port.
+ *
+ * Failure to destroy a flow rule handle may occur when other flow rules
+ * depend on it, and destroying it would result in an inconsistent state.
+ *
+ * This function is only guaranteed to succeed if handles are destroyed in
+ * reverse order of their creation.

How can the application find this information out on error?

Quoted 6 lines

Without maintaining a list, they cannot. The specified case is the only possible guarantee. That does not mean PMDs should not do their best to destroy flow rules, only that ordering must remain consistent in case of inability to destroy one.

What do you suggest?

I think if the app cannot remove a specific rule it may want to remove all rules and deal with flows in software for a time. So once the app knows it fails that should be enough.

Quoted 91 lines
Quoted 29 lines
Quoted 29 lines

+ *
+ * @param port_id
+ * Port identifier of Ethernet device.
+ * @param flow
+ * Flow rule handle to destroy.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +17 -0
+int
+rte_flow_destroy(uint8_t port_id,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *error);
+
+/**
+ * Destroy all flow rules associated with a port.
+ *
+ * In the unlikely event of failure, handles are still considered destroyed
+ * and no longer valid but the port must be assumed to be in an inconsistent
+ * state.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 6 lines

+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +3 -0
+int
+rte_flow_flush(uint8_t port_id,
+	       struct rte_flow_error *error);

rte_flow_destroy_all() would be more descriptive (but breaks your style)

There are enough underscores as it is. I like flush, if enough people complain we'll change it but it has to occur before the first public release.

Quoted 10 lines

lib/librte_ether/rte_flow.c

Quoted 7 lines
lib/librte_ether/rte_flow.c new file +7 -0
+
+/**
+ * Query an existing flow rule.
+ *
+ * This function allows retrieving flow-specific data such as counters.
+ * Data is gathered by special actions which must be present in the flow
+ * rule definition.

re last sentence, it would be good if you can put a link to RTE_FLOW_ACTION_TYPE_COUNT

Will do, I did not know how until very recently.

Quoted 11 lines
Quoted 9 lines

+ *
+ * @param port_id
+ * Port identifier of Ethernet device.
+ * @param flow
+ * Flow rule handle to query.
+ * @param action
+ * Action type to query.
+ * @param[in, out] data
+ * Pointer to storage for the associated query data type.

can this be anything other than rte_flow_query_count?

Likely in the future. I've only defined this one as a counterpart for existing API functionality and because we wanted to expose it in mlx5.

Quoted 12 lines
Quoted 12 lines

+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +6 -0
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       struct rte_flow_error *error);
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+
+#ifdef __cplusplus
+}
+#endif

I don't see a way to dump all the rules for a port out. I think this is neccessary for degbugging. You could have a look through dpif.h in OVS and see how dpif_flow_dump_next() is used, it might be a good reference.

Quoted 4 lines

DPDK does not maintain flow rules and, depending on hardware capabilities and level of compliance, PMDs do not necessarily do it either, particularly since it requires space and application probably have a better method to store these pointers for their own needs.

understood

Quoted 4 lines

What you see here is only a PMD interface. Depending on applications needs, generic helper functions built on top of these may be added to manage flow rules in the future.

I'm thinking of the case where something goes wrong and I want to get a dump of all the flow rules from hardware, not query the rules I think I have. I don't see a way to do it or something to build a helper on top of?

Quoted 11 lines
Quoted 5 lines

Also, it would be nice if there were an api that would allow a test packet to be injected and traced for debugging - although I'm not exactly sure how well it could be traced. For reference:
http://developers.redhat.com/blog/2016/10/12/tracing-packets-inside-open-vswitch/

Thanks for the link, I'm not sure how you'd do this either. Remember, as generic as it looks, this interface is only meant to configure the underlying device. You need to see it as one big offload, everything else is left to applications.

#69 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Adrien,
Thanks for sending out the patches,

Please find few comments below,

Regards
_Sugesh

Quoted 36 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Kevin Traynor
Sent: Friday, December 2, 2016 9:07 PM
To: Adrien Mazarguil <[email protected]>
Cc: [email protected]; Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>; [email protected]
Subject: Re: [dpdk-dev] [PATCH 01/22] ethdev: introduce generic flow API

Quoted 15 lines
Quoted 15 lines

Review

Quoted 12 lines
Quoted 1 line
Quoted 1 line

Snipp

+ *
+ * Attaches a 32 bit value to packets.
+ *
+ * This value is arbitrary and application-defined. For

(quoted diff) +7 -0
+compatibility with
+ * FDIR it is returned in the hash.fdir.hi mbuf field.
+PKT_RX_FDIR_ID is
+ * also set in ol_flags.
+ */
+struct rte_flow_action_mark {
+	uint32_t id; /**< 32 bit value to return with packets. */ };

One use case I thought we would be able to do for OVS is classification in hardware and the unique flow id is sent with the packet to

software.

Quoted 11 lines
Quoted 6 lines

But in OVS the ufid is 128 bits, so it means we can't and there is still the miniflow extract overhead. I'm not sure if there is a practical way around this.

Sugesh (cc'd) has looked at this before and may be able to comment or correct me.

Yes, we settled on 32 bit because currently no known hardware implementation supports more than this. If that changes, another action with a larger type shall be provided (no ABI breakage).

Quoted 5 lines
Quoted 3 lines

Also since even 64 bit would not be enough for the use case you mention, there is no choice but use this as an indirect value (such as an array or hash table index/value).

ok, cool. I think Sugesh has other ideas anyway!

[Sugesh] It should be fine with 32 bit . we can manage it in OVS accordingly.

Quoted 94 lines
Quoted 53 lines

[...]

Quoted 11 lines
Quoted 11 lines
lib/librte_ether/rte_flow.h new file +11 -0
+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_vf {
+	uint32_t original:1; /**< Use original VF ID if possible. */
+	uint32_t reserved:31; /**< Reserved, must be zero. */
+	uint32_t id; /**< VF ID to redirect packets to. */ };

[...]

Quoted 38 lines
Quoted 38 lines
lib/librte_ether/rte_flow.h new file +38 -0
+/**
+ * Check whether a flow rule can be created on a given port.
+ *
+ * While this function has no effect on the target device, the flow
+rule is
+ * validated against its current configuration state and the
+returned value
+ * should be considered valid by the caller for that state only.
+ *
+ * The returned value is guaranteed to remain valid only as long as
+no
+ * successful calls to rte_flow_create() or rte_flow_destroy() are
+made in
+ * the meantime and no device parameter affecting flow rules in any
+way are
+ * modified, due to possible collisions or resource limitations
+(although in
+ * such cases EINVAL should not be returned).
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 if flow rule is valid and can be created. A negative errno value
+ *   otherwise (rte_errno is also set), the following errors are defined:
+ *
+ *   -ENOSYS: underlying device does not support this functionality.
+ *
+ *   -EINVAL: unknown or invalid rule specification.
+ *
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

+ * -ENOTSUP: valid but unsupported rule specification (e.g. partial
+ * bit-masks are unsupported).
+ *
+ * -EEXIST: collision with an existing rule.
+ *
+ * -ENOMEM: not enough resources.
+ *
+ * -EBUSY: action cannot be performed due to busy device resources,

may

Quoted 1 line
Quoted 1 line
Quoted 1 line

+ * succeed if the affected queues or even the entire port are in a

stopped

Quoted 1 line
Quoted 1 line
Quoted 1 line

+ * state (see rte_eth_dev_rx_queue_stop() and

rte_eth_dev_stop()).

Quoted 28 lines
Quoted 11 lines

lib/librte_ether/rte_flow.h line 844

Quoted 7 lines

+ */

lib/librte_ether/rte_flow.h new file +6 -0
+int
+rte_flow_validate(uint8_t port_id,
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error);

Why not just use rte_flow_create() and get an error? Is it less disruptive to do a validate and find the rule cannot be created, than using a create directly?

The rationale can be found in the original RFC, which I'll convert to actual documentation in v2. In short:

- Calling rte_flow_validate() before rte_flow_create() is useless since rte_flow_create() also performs validation.

- We cannot possibly express a full static set of allowed flow rules, even if we could, it usually depends on the current hardware configuration therefore would not be static.

- rte_flow_validate() is thus provided as a replacement for capability flags. It can be used to determine during initialization if the underlying device can support the typical flow rules an application might want to provide later and do something useful with that information (e.g. always use software fallback due to HW limitations).

Quoted 4 lines
Quoted 2 lines

- rte_flow_validate() being a subset of rte_flow_create(), it is essentially free to expose.

make sense now, thanks.

[Sugesh] : We had this discussion earlier at the design stage about the time taken for programming the hardware, and how to make it deterministic. How about having a timeout parameter as well for the rte_flow_*
If the hardware flow insert is timed out, error out than waiting indefinitely, so that application have some control over The time to program the flow. It can be another set of APIs something like, rte_flow_create_timeout()

Are you going to provide any control over the initialization of NIC to define the capability matrices For eg; To operate in a L3 router mode, software wanted to initialize the NIC port only to consider the L2 and L3 fields. I assume the initialization is done based on the first rules that are programmed into the NIC.?

Quoted 48 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
lib/librte_ether/rte_flow.h new file +9 -0
+
+/**
+ * Create a flow rule on a given port.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

+ * Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ * Associated actions (list terminated by the END action).
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * A valid handle in case of success, NULL otherwise and rte_errno is

set

Quoted 14 lines
Quoted 5 lines
Quoted 5 lines

+ * to the positive version of one of the error codes defined for
+ * rte_flow_validate().
+ */

(quoted diff) +2 -0
+struct rte_flow *
+rte_flow_create(uint8_t port_id,
Quoted 8 lines

lib/librte_ether/rte_flow.h line 844

Quoted 4 lines
lib/librte_ether/rte_flow.h new file +4 -0
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error);

General question - are these functions threadsafe? In the OVS example you could have several threads wanting to create flow rules at the same time for same or different ports.

Quoted 7 lines

No they aren't, applications have to perform their own locking. The RFC (to be converted to actual documentation in v2) says that:

- API operations are synchronous and blocking (``EAGAIN`` cannot be returned).

- There is no provision for reentrancy/multi-thread safety, although

nothing

Quoted 2 lines

should prevent different devices from being configured at the same time. PMDs may protect their control path functions accordingly.

other comment above wrt locking.

Quoted 1 line
#70 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Sugesh,

On Tue, Dec 06, 2016 at 06:11:38PM +0000, Chandran, Sugesh wrote:
[...]

Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
Quoted 10 lines

lib/librte_ether/rte_flow.h line 844

Quoted 6 lines
lib/librte_ether/rte_flow.h new file +6 -0
+int
+rte_flow_validate(uint8_t port_id,
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error);

Why not just use rte_flow_create() and get an error? Is it less disruptive to do a validate and find the rule cannot be created, than using a create directly?

The rationale can be found in the original RFC, which I'll convert to actual documentation in v2. In short:

- Calling rte_flow_validate() before rte_flow_create() is useless since rte_flow_create() also performs validation.

- We cannot possibly express a full static set of allowed flow rules, even if we could, it usually depends on the current hardware configuration therefore would not be static.

- rte_flow_validate() is thus provided as a replacement for capability flags. It can be used to determine during initialization if the underlying device can support the typical flow rules an application might want to provide later and do something useful with that information (e.g. always use software fallback due to HW limitations).

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

- rte_flow_validate() being a subset of rte_flow_create(), it is essentially free to expose.

make sense now, thanks.

[Sugesh] : We had this discussion earlier at the design stage about the time taken for programming the hardware, and how to make it deterministic. How about having a timeout parameter as well for the rte_flow_*
If the hardware flow insert is timed out, error out than waiting indefinitely, so that application have some control over The time to program the flow. It can be another set of APIs something like, rte_flow_create_timeout()

Yes as discussed the existing API does not provide any timing constraints to PMDs, validate() and create() may take forever to complete, although PMDs are strongly encouraged to take as little time as possible.

Like you suggested, this could be done through distinct API calls. The validate() function would also have its _timeout() counterpart since the set of possible rules could be restricted in that mode.

Quoted 3 lines

Are you going to provide any control over the initialization of NIC to define the capability matrices For eg; To operate in a L3 router mode, software wanted to initialize the NIC port only to consider the L2 and L3 fields. I assume the initialization is done based on the first rules that are programmed into the NIC.?

Precisely, PMDs are supposed to determine the most appropriate device mode to use in order to handle the requested rules. They may even switch to another mode if necessary assuming this does not break existing constraints.

I think we've discussed an atomic (commit-based) mode of operation through separate functions as well, where the application would attempt to create a bunch of rules at once, possibly making it easier for PMDs to determine the most appropriate mode of operation for the device.

All of these may be added later according to users feedback once the basic API has settled.

Signature · 2 lines
Adrien Mazarguil
6WIND
#71 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Adrien,
Thank you for your comments,
Please see the reply below.

Regards
_Sugesh

Quoted 55 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Thursday, December 8, 2016 3:09 PM
To: Chandran, Sugesh <[email protected]>
Cc: Kevin Traynor <[email protected]>; [email protected]; Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>; [email protected]
Subject: Re: [dpdk-dev] [PATCH 01/22] ethdev: introduce generic flow API

Hi Sugesh,

On Tue, Dec 06, 2016 at 06:11:38PM +0000, Chandran, Sugesh wrote:
[...]

Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 10 lines

lib/librte_ether/rte_flow.h line 844

Quoted 6 lines
lib/librte_ether/rte_flow.h new file +6 -0
+int
+rte_flow_validate(uint8_t port_id,
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error);

Why not just use rte_flow_create() and get an error? Is it less disruptive to do a validate and find the rule cannot be created, than using a create directly?

The rationale can be found in the original RFC, which I'll convert to actual documentation in v2. In short:

- Calling rte_flow_validate() before rte_flow_create() is useless since rte_flow_create() also performs validation.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

- We cannot possibly express a full static set of allowed flow rules, even if we could, it usually depends on the current hardware configuration therefore would not be static.

- rte_flow_validate() is thus provided as a replacement for capability flags. It can be used to determine during initialization if the underlying device can support the typical flow rules an application might want to provide later and do something useful with that information (e.g.

always

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

use software fallback due to HW limitations).

- rte_flow_validate() being a subset of rte_flow_create(), it is

essentially

Quoted 10 lines
Quoted 3 lines
Quoted 1 line

free to expose.

make sense now, thanks.

[Sugesh] : We had this discussion earlier at the design stage about the time taken for programming the hardware, and how to make it deterministic. How about having a timeout parameter as well for the rte_flow_* If the hardware flow insert is timed out, error out than waiting indefinitely, so that application have some control over The time to program the flow. It can be another set of APIs something like, rte_flow_create_timeout()

Quoted 7 lines

Yes as discussed the existing API does not provide any timing constraints to PMDs, validate() and create() may take forever to complete, although PMDs are strongly encouraged to take as little time as possible.

Like you suggested, this could be done through distinct API calls. The validate() function would also have its _timeout() counterpart since the set of possible rules could be restricted in that mode.

[Sugesh] Thanks!. Looking forward to see an api set with that implementation as well in the future :). I feel it's a must from the user application point of view.

Quoted 13 lines
Quoted 3 lines

Are you going to provide any control over the initialization of NIC to define the capability matrices For eg; To operate in a L3 router mode,

software wanted to initialize the NIC port only to consider the L2 and L3 fields.

Quoted 1 line

I assume the initialization is done based on the first rules that are

programmed into the NIC.?

Precisely, PMDs are supposed to determine the most appropriate device mode to use in order to handle the requested rules. They may even switch to another mode if necessary assuming this does not break existing constraints.

Quoted 7 lines

I think we've discussed an atomic (commit-based) mode of operation through separate functions as well, where the application would attempt to create a bunch of rules at once, possibly making it easier for PMDs to determine the most appropriate mode of operation for the device.

All of these may be added later according to users feedback once the basic API has settled.

[Sugesh] Yes , we discussed about this before. However I feel that, it make sense
to provide some flexibility to the user/application to define a profile/mode of the device. This way the complexity of determining the mode by itself will be taken away from PMD. Looking at the P4 enablement patches in OVS, the mode definition APIs can be used in conjunction P4 behavioral model.
For eg: A P4 model for a L2 switch operate OVS as a L2 switch. Using the mode definition APIs Its possible to impose the same behavioral model in the hardware too.
This way its simple, clean and very predictive though it needs to define an additional profile_define APIs. I am sorry to provide the comment at this stage, However looking at the adoption of ebpf, P4 make me to think this way.
What do you think?

Quoted 4 lines

--
Adrien Mazarguil
6WIND

#72 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Sugesh,

On Fri, Dec 09, 2016 at 12:18:03PM +0000, Chandran, Sugesh wrote:
[...]

Quoted 19 lines
Quoted 12 lines
Quoted 2 lines

Are you going to provide any control over the initialization of NIC to define the capability matrices For eg; To operate in a L3 router mode,

software wanted to initialize the NIC port only to consider the L2 and L3 fields.

Quoted 1 line

I assume the initialization is done based on the first rules that are

programmed into the NIC.?

Precisely, PMDs are supposed to determine the most appropriate device mode to use in order to handle the requested rules. They may even switch to another mode if necessary assuming this does not break existing constraints.

Quoted 7 lines

I think we've discussed an atomic (commit-based) mode of operation through separate functions as well, where the application would attempt to create a bunch of rules at once, possibly making it easier for PMDs to determine the most appropriate mode of operation for the device.

All of these may be added later according to users feedback once the basic API has settled.

Quoted 11 lines

[Sugesh] Yes , we discussed about this before. However I feel that, it make sense
to provide some flexibility to the user/application to define a profile/mode of the device. This way the complexity of determining the mode by itself will be taken away from PMD. Looking at the P4 enablement patches in OVS, the mode definition APIs can be used in conjunction P4 behavioral model.
For eg: A P4 model for a L2 switch operate OVS as a L2 switch. Using the mode definition APIs Its possible to impose the same behavioral model in the hardware too.
This way its simple, clean and very predictive though it needs to define an additional profile_define APIs. I am sorry to provide the comment at this stage, However looking at the adoption of ebpf, P4 make me to think this way.
What do you think?

What you suggest (device profile configuration) would be done by a separate function in any case, so as long as everyone agrees on a generic method to do so, no problem with extending rte_flow. By default in the meantime we'll have to rely on PMDs to make the right decision.

Do you think it has to be defined from the beginning?

Signature · 2 lines
Adrien Mazarguil
6WIND
#73 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Adrien,

Regards
_Sugesh

Quoted 49 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Friday, December 9, 2016 4:39 PM
To: Chandran, Sugesh <[email protected]>
Cc: Kevin Traynor <[email protected]>; [email protected]; Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>; [email protected]
Subject: Re: [dpdk-dev] [PATCH 01/22] ethdev: introduce generic flow API

Hi Sugesh,

On Fri, Dec 09, 2016 at 12:18:03PM +0000, Chandran, Sugesh wrote:
[...]

Quoted 14 lines
Quoted 14 lines
Quoted 3 lines

Are you going to provide any control over the initialization of NIC to define the capability matrices For eg; To operate in a L3 router mode,

software wanted to initialize the NIC port only to consider the L2 and L3 fields.

Quoted 2 lines

I assume the initialization is done based on the first rules that are

programmed into the NIC.?

Precisely, PMDs are supposed to determine the most appropriate device mode to use in order to handle the requested rules. They may even switch to another mode if necessary assuming this does not break existing constraints.

Quoted 4 lines
Quoted 4 lines

I think we've discussed an atomic (commit-based) mode of operation through separate functions as well, where the application would attempt to create a bunch of rules at once, possibly making it easier for PMDs to determine the most appropriate mode of operation

for the device.

Quoted 5 lines
Quoted 3 lines

All of these may be added later according to users feedback once the basic API has settled.

[Sugesh] Yes , we discussed about this before. However I feel that, it make sense to provide some flexibility to the user/application to define a

profile/mode of the device.

Quoted 1 line

This way the complexity of determining the mode by itself will be taken

away from PMD.

Quoted 5 lines

Looking at the P4 enablement patches in OVS, the mode definition APIs can be used in conjunction
P4 behavioral model.
For eg: A P4 model for a L2 switch operate OVS as a L2 switch. Using the mode definition APIs Its possible to impose the same behavioral model

in the hardware too.

Quoted 1 line

This way its simple, clean and very predictive though it needs to define an

additional profile_define APIs.

Quoted 8 lines
Quoted 3 lines

I am sorry to provide the comment at this stage, However looking at the adoption of ebpf, P4 make me to think this way.
What do you think?

What you suggest (device profile configuration) would be done by a separate function in any case, so as long as everyone agrees on a generic method to do so, no problem with extending rte_flow. By default in the meantime we'll have to rely on PMDs to make the right decision.

[Sugesh] I am fine with PMD is making the decision on profile/mode selection in Default case. However we must provide an option for the application to define a mode and PMD must honor with it to avoid making an invalid mode change.

Quoted 2 lines

Do you think it has to be defined from the beginning?

[Sugesh] I feel it's going to be another big topic to decide how proposed mode implementation will be looks like, What should be available modes and etc. So I am OK to consider as its not part of this flow API definition for now. However its good to mention that in the API comments section to be aware. Do you agree that?

Quoted 4 lines

--
Adrien Mazarguil
6WIND

#74 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Sugesh,

Quoted 62 lines · Sugesh

Hi Adrien,

Regards
_Sugesh

Quoted 49 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Friday, December 9, 2016 4:39 PM
To: Chandran, Sugesh <[email protected]>
Cc: Kevin Traynor <[email protected]>; [email protected]; Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>; [email protected]
Subject: Re: [dpdk-dev] [PATCH 01/22] ethdev: introduce generic flow API

Hi Sugesh,

On Fri, Dec 09, 2016 at 12:18:03PM +0000, Chandran, Sugesh wrote:
[...]

Quoted 14 lines
Quoted 14 lines
Quoted 3 lines

Are you going to provide any control over the initialization of NIC to define the capability matrices For eg; To operate in a L3 router mode,

software wanted to initialize the NIC port only to consider the L2 and L3 fields.

Quoted 2 lines

I assume the initialization is done based on the first rules that are

programmed into the NIC.?

Precisely, PMDs are supposed to determine the most appropriate device mode to use in order to handle the requested rules. They may even switch to another mode if necessary assuming this does not break existing constraints.

Quoted 4 lines
Quoted 4 lines

I think we've discussed an atomic (commit-based) mode of operation through separate functions as well, where the application would attempt to create a bunch of rules at once, possibly making it easier for PMDs to determine the most appropriate mode of operation

for the device.

Quoted 5 lines
Quoted 3 lines

All of these may be added later according to users feedback once the basic API has settled.

[Sugesh] Yes , we discussed about this before. However I feel that, it make sense to provide some flexibility to the user/application to define a

profile/mode of the device.

Quoted 1 line

This way the complexity of determining the mode by itself will be taken

away from PMD.

Quoted 5 lines

Looking at the P4 enablement patches in OVS, the mode definition APIs can be used in conjunction
P4 behavioral model.
For eg: A P4 model for a L2 switch operate OVS as a L2 switch. Using the mode definition APIs Its possible to impose the same behavioral model

in the hardware too.

Quoted 1 line

This way its simple, clean and very predictive though it needs to define an

additional profile_define APIs.

Quoted 8 lines
Quoted 3 lines

I am sorry to provide the comment at this stage, However looking at the adoption of ebpf, P4 make me to think this way.
What do you think?

What you suggest (device profile configuration) would be done by a separate function in any case, so as long as everyone agrees on a generic method to do so, no problem with extending rte_flow. By default in the meantime we'll have to rely on PMDs to make the right decision.

Quoted 8 lines

[Sugesh] I am fine with PMD is making the decision on profile/mode selection in Default case. However we must provide an option for the application to define a mode and PMD must honor with it to avoid making an invalid mode change.

Quoted 2 lines

Do you think it has to be defined from the beginning?

[Sugesh] I feel it's going to be another big topic to decide how proposed mode implementation will be looks like, What should be available modes and etc. So I am OK to consider as its not part of this flow API definition for now. However its good to mention that in the API comments section to be aware. Do you agree that?

Will do, I'll mention it in the "future evolutions" section.

Signature · 2 lines
Adrien Mazarguil
6WIND
#75 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

In reply to Kevin Traynor
Quoted 88 lines · Kevin Traynor
Quoted 80 lines · Adrien Mazarguil

Hi Kevin,

Quoted 16 lines · Kevin Traynor

Hi Adrien,

Quoted 13 lines · Adrien Mazarguil

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

Quoted 8 lines
Quoted 5 lines

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

I'd suggest to add a deprecation notice to deprecation.rst, ideally with a target release.

Will do, not a sure about the target release though. It seems a bit early since no PMD really supports this API yet.

[...]

Quoted 6 lines
Quoted 6 lines
lib/librte_ether/rte_flow.c new file +0 -0
new file mode 100644
index 0000000..064963d
@@ -0,0 +1,159 @@
Quoted 8 lines

lib/librte_ether/rte_flow.c line 5

Quoted 5 lines
lib/librte_ether/rte_flow.c new file +5 -0
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright 2016 6WIND S.A.
+ *   Copyright 2016 Mellanox.

There's Mellanox copyright but you are the only signed-off-by - is that right?

Yes, I'm the primary maintainer for Mellanox PMDs and this API was designed on their behalf to expose several features from mlx4/mlx5 as the existing filter types had too many limitations.

[...]

Quoted 19 lines
Quoted 19 lines
lib/librte_ether/rte_flow.c new file +19 -0
+/* Get generic flow operations structure from a port. */
+const struct rte_flow_ops *
+rte_flow_ops_get(uint8_t port_id, struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops;
+	int code;
+
+	if (unlikely(!rte_eth_dev_is_valid_port(port_id)))
+		code = ENODEV;
+	else if (unlikely(!dev->dev_ops->filter_ctrl ||
+			  dev->dev_ops->filter_ctrl(dev,
+						    RTE_ETH_FILTER_GENERIC,
+						    RTE_ETH_FILTER_GET,
+						    &ops) ||
+			  !ops))
+		code = ENOTSUP;
+	else
+		return ops;
Quoted 8 lines

lib/librte_ether/rte_flow.c line 65

Quoted 5 lines
lib/librte_ether/rte_flow.c new file +5 -0
+	rte_flow_error_set(error, code, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(code));
+	return NULL;
+}
+

Is it expected that the application or pmd will provide locking between these functions if required? I think it's going to have to be the app.

Quoted 6 lines

Locking is indeed expected to be performed by applications. This API only documents places where locking would make sense if necessary and expected behavior.

Like all control path APIs, this one assumes a single control thread. Applications must take the necessary precautions.

Quoted 6 lines

If you look at OVS now it's quite possible that you have 2 rx queues serviced by different threads, that would also install the flow rules in the software flow caches - possibly that could extend to adding hardware flows. There could also be another thread that is querying for stats. So anything that can be done to minimise the locking would be helpful - maybe query() could be atomic and not require any locking?

I think we need basic functions with as few constraints as possible on PMDs first, this API being somewhat complex to implement on their side. That covers the common use case where applications have a single control thread or otherwise perform locking on their own.

Once the basics are there for most PMDs, we may add new functions, items, properties and actions that provide additional constraints (timing, multi-threading and so on), which remain to be defined according to feedback. It is designed to be extended without causing ABI breakage.

As for query(), let's see how PMDs handle it first. A race between query() and create() on a given device is almost unavoidable without locking, same for queries that reset counters in a given flow rule. Basic parallel queries should not cause any harm otherwise, although this cannot be guaranteed yet.

Quoted 121 lines
Quoted 36 lines

[...]

Quoted 22 lines
Quoted 13 lines

+/**
+ * Flow rule attributes.
+ *
+ * Priorities are set on two levels: per group and per rule within groups.
+ *
+ * Lower values denote higher priority, the highest priority for both levels
+ * is 0, so that a rule with priority 0 in group 8 is always matched after a
+ * rule with priority 8 in group 0.
+ *
+ * Although optional, applications are encouraged to group similar rules as
+ * much as possible to fully take advantage of hardware capabilities
+ * (e.g. optimized matching) and work around limitations (e.g. a single
+ * pattern type possibly allowed in a given group).

Quoted 8 lines

+ *
+ * Group and priority levels are arbitrary and up to the application, they
+ * do not need to be contiguous nor start from 0, however the maximum number
+ * varies between devices and may be affected by existing flow rules.
+ *
+ * If a packet is matched by several rules of a given group for a given
+ * priority level, the outcome is undefined. It can take any path, may be
+ * duplicated or even cause unrecoverable errors.

Quoted 6 lines

I get what you are trying to do here wrt supporting multiple pmds/hardware implementations and it's a good idea to keep it flexible.

Given that the outcome is undefined, it would be nice that the application has a way of finding the specific effects for verification and debugging.

Right, however it was deemed a bit difficult to manage in many cases hence the vagueness.

For example, suppose two rules with the same group and priority, one matching any IPv4 header, the other one any UDP header:

Quoted 8 lines

- TCPv4 packets => rule #1.
- UDPv6 packets => rule #2.
- UDPv4 packets => both?

That last one is perhaps invalid, checking that some unspecified protocol combination does not overlap is expensive and may miss corner cases, even assuming this is not an issue, what if the application guarantees that no UDPv4 packets can ever hit that rule?

that's fine - I don't expect the software to be able to know what the hardware will do with those rules. It's more about trying to get a dump from the hardware if something goes wrong. Anyway covered in comment later.

Quoted 72 lines

Suggestions are welcome though, perhaps we can refine the description

Quoted 21 lines
Quoted 21 lines

+ *
+ * Note that support for more than a single group and priority level is not
+ * guaranteed.
+ *
+ * Flow rules can apply to inbound and/or outbound traffic (ingress/egress).
+ *
+ * Several pattern items and actions are valid and can be used in both
+ * directions. Those valid for only one direction are described as such.
+ *
+ * Specifying both directions at once is not recommended but may be valid in
+ * some cases, such as incrementing the same counter twice.
+ *
+ * Not specifying any direction is currently an error.
+ */

lib/librte_ether/rte_flow.c new file +7 -0
+struct rte_flow_attr {
+	uint32_t group; /**< Priority group. */
+	uint32_t priority; /**< Priority level within group. */
+	uint32_t ingress:1; /**< Rule applies to ingress traffic. */
+	uint32_t egress:1; /**< Rule applies to egress traffic. */
+	uint32_t reserved:30; /**< Reserved, must be zero. */
+};

[...]

Quoted 18 lines
Quoted 9 lines

+/**
+ * RTE_FLOW_ITEM_TYPE_VF
+ *
+ * Matches packets addressed to a virtual function ID of the device.
+ *
+ * If the underlying device function differs from the one that would
+ * normally receive the matched traffic, specifying this item prevents it
+ * from reaching that device unless the flow rule contains a VF
+ * action. Packets are not duplicated between device instances by default.

Quoted 8 lines

+ *
+ * - Likely to return an error or never match any traffic if this causes a
+ * VF device to match traffic addressed to a different VF.
+ * - Can be specified multiple times to match traffic addressed to several
+ * specific VFs.
+ * - Can be combined with a PF item to match both PF and VF traffic.
+ *
+ * A zeroed mask can be used to match any VF.

Quoted 1 line

can you refer explicitly to id

If you mean "VF" to "VF ID" then yes, will do it for v2.

Quoted 4 lines
Quoted 4 lines

+ */

lib/librte_ether/rte_flow.c new file +3 -0
+struct rte_flow_item_vf {
+	uint32_t id; /**< Destination VF ID. */
+};

[...]

Quoted 20 lines
Quoted 19 lines

+/**
+ * Matching pattern item definition.
+ *
+ * A pattern is formed by stacking items starting from the lowest protocol
+ * layer to match. This stacking restriction does not apply to meta items
+ * which can be placed anywhere in the stack with no effect on the meaning
+ * of the resulting pattern.
+ *
+ * A stack is terminated by a END item.
+ *
+ * The spec field should be a valid pointer to a structure of the related
+ * item type. It may be set to NULL in many cases to use default values.
+ *
+ * Optionally, last can point to a structure of the same type to define an
+ * inclusive range. This is mostly supported by integer and address fields,
+ * may cause errors otherwise. Fields that do not support ranges must be set
+ * to the same value as their spec counterparts.
+ *
+ * By default all fields present in spec are considered relevant.* This

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

typo "*"

No, that's an asterisk for a footnote below. Perhaps it is a bit unusual, would something like "[1]" look better?

oh, I thought it was the start of a comment line gone astray. Maybe "See note below", no big deal though.

OK, will change it anyway for clarity.

Quoted 300 lines
Quoted 55 lines
Quoted 11 lines
Quoted 11 lines

+ * behavior can be altered by providing a mask structure of the same type
+ * with applicable bits set to one. It can also be used to partially filter
+ * out specific fields (e.g. as an alternate mean to match ranges of IP
+ * addresses).
+ *
+ * Note this is a simple bit-mask applied before interpreting the contents
+ * of spec and last, which may yield unexpected results if not used
+ * carefully. For example, if for an IPv4 address field, spec provides
+ * 10.1.2.3, last provides 10.3.4.5 and mask provides 255.255.0.0, the
+ * effective range is 10.1.0.0 to 10.3.255.255.
+ *

See footnote below:

Quoted 32 lines
Quoted 32 lines

+ * * The defaults for data-matching items such as IPv4 when mask is not
+ * specified actually depend on the underlying implementation since only
+ * recognized fields can be taken into account.
+ */

lib/librte_ether/rte_flow.c new file +28 -0
+struct rte_flow_item {
+	enum rte_flow_item_type type; /**< Item type. */
+	const void *spec; /**< Pointer to item specification structure. */
+	const void *last; /**< Defines an inclusive range (spec to last). */
+	const void *mask; /**< Bit-mask applied to spec and last. */
+};
+
+/**
+ * Action types.
+ *
+ * Each possible action is represented by a type. Some have associated
+ * configuration structures. Several actions combined in a list can be
+ * affected to a flow rule. That list is not ordered.
+ *
+ * They fall in three categories:
+ *
+ * - Terminating actions (such as QUEUE, DROP, RSS, PF, VF) that prevent
+ *   processing matched packets by subsequent flow rules, unless overridden
+ *   with PASSTHRU.
+ *
+ * - Non terminating actions (PASSTHRU, DUP) that leave matched packets up
+ *   for additional processing by subsequent flow rules.
+ *
+ * - Other non terminating meta actions that do not affect the fate of
+ *   packets (END, VOID, MARK, FLAG, COUNT).
+ *
+ * When several actions are combined in a flow rule, they should all have
+ * different types (e.g. dropping a packet twice is not possible). The
Quoted 8 lines
Quoted 3 lines

+ * defined behavior is for PMDs to only take into account the last action of
+ * a given type found in the list. PMDs still perform error checking on the
+ * entire list.

why do you define that the pmd will interpret multiple same type rules in this way...would it not make more sense for the pmd to just return EINVAL for an invalid set of rules? It seems more transparent for the application.

Quoted 6 lines

Well, I had to define something as a default. The reason is that any number of VOID actions may specified and did not want that to be a special case in order to keep PMD parsers as simple as possible. I'll settle for EINVAL (or some other error) if at least one PMD maintainer other than Nelio who intends to implement this API is not convinced by this explanation, all right?

From an API perspective I think it's cleaner to pass or fail with the input rather than change it. But yes, please take pmd maintainers input as to what is reasonable to check also.

Quoted 24 lines

[...]

Quoted 19 lines

lib/librte_ether/rte_flow.c

Quoted 12 lines
lib/librte_ether/rte_flow.c new file +12 -0
+/**
+ * RTE_FLOW_ACTION_TYPE_MARK
+ *
+ * Attaches a 32 bit value to packets.
+ *
+ * This value is arbitrary and application-defined. For compatibility with
+ * FDIR it is returned in the hash.fdir.hi mbuf field. PKT_RX_FDIR_ID is
+ * also set in ol_flags.
+ */
+struct rte_flow_action_mark {
+	uint32_t id; /**< 32 bit value to return with packets. */
+};

One use case I thought we would be able to do for OVS is classification in hardware and the unique flow id is sent with the packet to software. But in OVS the ufid is 128 bits, so it means we can't and there is still the miniflow extract overhead. I'm not sure if there is a practical way around this.

Quoted 2 lines

Sugesh (cc'd) has looked at this before and may be able to comment or correct me.

Quoted 7 lines

Yes, we settled on 32 bit because currently no known hardware implementation supports more than this. If that changes, another action with a larger type shall be provided (no ABI breakage).

Also since even 64 bit would not be enough for the use case you mention, there is no choice but use this as an indirect value (such as an array or hash table index/value).

ok, cool. I think Sugesh has other ideas anyway!

Quoted 42 lines

[...]

Quoted 13 lines
Quoted 13 lines

+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets to
+ * spread them among several queues according to the provided parameters.
+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf field,
+ * however it conflicts with the MARK action as they share the same
+ * space. When both actions are specified, the RSS hash is discarded and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.

Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 5 lines

+ */

lib/librte_ether/rte_flow.c new file +4 -0
+struct rte_flow_action_rss {
+	const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+	uint16_t queues; /**< Number of entries in queue[]. */
+	uint16_t queue[]; /**< Queues indices to use. */

I'd try and avoid queue and queues - someone will say "huh?" when reading code. s/queues/num ?

Agreed, will update for v2.

Quoted 7 lines
Quoted 7 lines
lib/librte_ether/rte_flow.c new file +7 -0
+};
+
+/**
+ * RTE_FLOW_ACTION_TYPE_VF
+ *
+ * Redirects packets to a virtual function (VF) of the current device.
+ *
Quoted 8 lines
Quoted 5 lines

+ * Packets matched by a VF pattern item can be redirected to their original
+ * VF ID instead of the specified one. This parameter may not be available
+ * and is not guaranteed to work properly if the VF part is matched by a
+ * prior flow rule or if packets are not addressed to a VF in the first
+ * place.

Not clear what you mean by "not guaranteed to work if...". Please return fail when this action is used if this is not going to work.

Quoted 7 lines

Again, this is a case where it is difficult for a PMD to determine if the entire list of flow rules makes sense. Perhaps it does, perhaps whatever goes through has already been filtered out of possible issues.

Here the documentation states the precautions an application should take to guarantee it will work as intended. Perhaps it can be reworded (any suggestion?), but a PMD can certainly not provide any strong guarantee.

I see your point. Maybe for easy check things the pmd would return fail, but for more complex I agree it's too difficult.

Quoted 74 lines
Quoted 9 lines
Quoted 9 lines

+ *
+ * Terminating by default.
+ */

lib/librte_ether/rte_flow.c new file +5 -0
+struct rte_flow_action_vf {
+	uint32_t original:1; /**< Use original VF ID if possible. */
+	uint32_t reserved:31; /**< Reserved, must be zero. */
+	uint32_t id; /**< VF ID to redirect packets to. */
+};

[...]

Quoted 45 lines
Quoted 45 lines
lib/librte_ether/rte_flow.c new file +45 -0
+/**
+ * Check whether a flow rule can be created on a given port.
+ *
+ * While this function has no effect on the target device, the flow rule is
+ * validated against its current configuration state and the returned value
+ * should be considered valid by the caller for that state only.
+ *
+ * The returned value is guaranteed to remain valid only as long as no
+ * successful calls to rte_flow_create() or rte_flow_destroy() are made in
+ * the meantime and no device parameter affecting flow rules in any way are
+ * modified, due to possible collisions or resource limitations (although in
+ * such cases EINVAL should not be returned).
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 if flow rule is valid and can be created. A negative errno value
+ *   otherwise (rte_errno is also set), the following errors are defined:
+ *
+ *   -ENOSYS: underlying device does not support this functionality.
+ *
+ *   -EINVAL: unknown or invalid rule specification.
+ *
+ *   -ENOTSUP: valid but unsupported rule specification (e.g. partial
+ *   bit-masks are unsupported).
+ *
+ *   -EEXIST: collision with an existing rule.
+ *
+ *   -ENOMEM: not enough resources.
+ *
+ *   -EBUSY: action cannot be performed due to busy device resources, may
+ *   succeed if the affected queues or even the entire port are in a stopped
+ *   state (see rte_eth_dev_rx_queue_stop() and rte_eth_dev_stop()).
+ */
+int
+rte_flow_validate(uint8_t port_id,
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+		  const struct rte_flow_attr *attr,
+		  const struct rte_flow_item pattern[],
+		  const struct rte_flow_action actions[],
+		  struct rte_flow_error *error);

Why not just use rte_flow_create() and get an error? Is it less disruptive to do a validate and find the rule cannot be created, than using a create directly?

The rationale can be found in the original RFC, which I'll convert to actual documentation in v2. In short:

- Calling rte_flow_validate() before rte_flow_create() is useless since rte_flow_create() also performs validation.

- We cannot possibly express a full static set of allowed flow rules, even if we could, it usually depends on the current hardware configuration therefore would not be static.

Quoted 8 lines

- rte_flow_validate() is thus provided as a replacement for capability flags. It can be used to determine during initialization if the underlying device can support the typical flow rules an application might want to provide later and do something useful with that information (e.g. always use software fallback due to HW limitations).

- rte_flow_validate() being a subset of rte_flow_create(), it is essentially free to expose.

make sense now, thanks.

Quoted 35 lines
Quoted 23 lines
Quoted 23 lines
lib/librte_ether/rte_flow.c new file +22 -0
+
+/**
+ * Create a flow rule on a given port.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] attr
+ *   Flow rule attributes.
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END action).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   A valid handle in case of success, NULL otherwise and rte_errno is set
+ *   to the positive version of one of the error codes defined for
+ *   rte_flow_validate().
+ */
+struct rte_flow *
+rte_flow_create(uint8_t port_id,
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error);

General question - are these functions threadsafe? In the OVS example you could have several threads wanting to create flow rules at the same time for same or different ports.

No they aren't, applications have to perform their own locking. The RFC (to be converted to actual documentation in v2) says that:

Quoted 6 lines

- API operations are synchronous and blocking (``EAGAIN`` cannot be returned).

- There is no provision for reentrancy/multi-thread safety, although nothing should prevent different devices from being configured at the same time. PMDs may protect their control path functions accordingly.

other comment above wrt locking.

Quoted 18 lines
Quoted 12 lines

lib/librte_ether/rte_flow.c

Quoted 10 lines
lib/librte_ether/rte_flow.c new file +9 -0
+
+/**
+ * Destroy a flow rule on a given port.
+ *
+ * Failure to destroy a flow rule handle may occur when other flow rules
+ * depend on it, and destroying it would result in an inconsistent state.
+ *
+ * This function is only guaranteed to succeed if handles are destroyed in
+ * reverse order of their creation.

How can the application find this information out on error?

Without maintaining a list, they cannot. The specified case is the only possible guarantee. That does not mean PMDs should not do their best to destroy flow rules, only that ordering must remain consistent in case of inability to destroy one.

Quoted 5 lines
Quoted 1 line

What do you suggest?

I think if the app cannot remove a specific rule it may want to remove all rules and deal with flows in software for a time. So once the app knows it fails that should be enough.

OK, then since destruction may return an error already, is it fine? Applications may call rte_flow_flush() (not supposed to fail unless there is a serious issue, abort() in that case) and switch to SW fallback.

Quoted 98 lines
Quoted 90 lines
Quoted 28 lines
Quoted 28 lines

+ *
+ * @param port_id
+ * Port identifier of Ethernet device.
+ * @param flow
+ * Flow rule handle to destroy.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +17 -0
+int
+rte_flow_destroy(uint8_t port_id,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *error);
+
+/**
+ * Destroy all flow rules associated with a port.
+ *
+ * In the unlikely event of failure, handles are still considered destroyed
+ * and no longer valid but the port must be assumed to be in an inconsistent
+ * state.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 6 lines

+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +3 -0
+int
+rte_flow_flush(uint8_t port_id,
+	       struct rte_flow_error *error);

rte_flow_destroy_all() would be more descriptive (but breaks your style)

There are enough underscores as it is. I like flush, if enough people complain we'll change it but it has to occur before the first public release.

Quoted 10 lines

lib/librte_ether/rte_flow.c

Quoted 7 lines
lib/librte_ether/rte_flow.c new file +7 -0
+
+/**
+ * Query an existing flow rule.
+ *
+ * This function allows retrieving flow-specific data such as counters.
+ * Data is gathered by special actions which must be present in the flow
+ * rule definition.

re last sentence, it would be good if you can put a link to RTE_FLOW_ACTION_TYPE_COUNT

Will do, I did not know how until very recently.

Quoted 11 lines
Quoted 9 lines

+ *
+ * @param port_id
+ * Port identifier of Ethernet device.
+ * @param flow
+ * Flow rule handle to query.
+ * @param action
+ * Action type to query.
+ * @param[in, out] data
+ * Pointer to storage for the associated query data type.

can this be anything other than rte_flow_query_count?

Likely in the future. I've only defined this one as a counterpart for existing API functionality and because we wanted to expose it in mlx5.

Quoted 12 lines
Quoted 12 lines

+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.c new file +6 -0
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       struct rte_flow_error *error);
Quoted 8 lines

lib/librte_ether/rte_flow.c

Quoted 4 lines
lib/librte_ether/rte_flow.c new file +4 -0
+
+#ifdef __cplusplus
+}
+#endif

I don't see a way to dump all the rules for a port out. I think this is neccessary for degbugging. You could have a look through dpif.h in OVS and see how dpif_flow_dump_next() is used, it might be a good reference.

Quoted 4 lines

DPDK does not maintain flow rules and, depending on hardware capabilities and level of compliance, PMDs do not necessarily do it either, particularly since it requires space and application probably have a better method to store these pointers for their own needs.

understood

Quoted 1 line
Quoted 7 lines
Quoted 3 lines

What you see here is only a PMD interface. Depending on applications needs, generic helper functions built on top of these may be added to manage flow rules in the future.

I'm thinking of the case where something goes wrong and I want to get a dump of all the flow rules from hardware, not query the rules I think I have. I don't see a way to do it or something to build a helper on top of?

Generic helper functions would exist on top of this API and would likely maintain a list of flow rules themselves. The dump in that case would be entirely implemented in software. I think that recovering flow rules from HW may be complicated in many cases (even without taking storage allocation and rules conversion issues into account), therefore if there is really a need for it, we could perhaps add a dump() function that PMDs are free to implement later.

Signature · 2 lines
Adrien Mazarguil
6WIND
#76 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

hi Adrien, sorry for the delay

<...>

Quoted 23 lines
Quoted 11 lines
Quoted 10 lines
Quoted 3 lines

Is it expected that the application or pmd will provide locking between these functions if required? I think it's going to have to be the app.

Locking is indeed expected to be performed by applications. This API only documents places where locking would make sense if necessary and expected behavior.

Like all control path APIs, this one assumes a single control thread. Applications must take the necessary precautions.

Quoted 6 lines

If you look at OVS now it's quite possible that you have 2 rx queues serviced by different threads, that would also install the flow rules in the software flow caches - possibly that could extend to adding hardware flows. There could also be another thread that is querying for stats. So anything that can be done to minimise the locking would be helpful - maybe query() could be atomic and not require any locking?

I think we need basic functions with as few constraints as possible on PMDs first, this API being somewhat complex to implement on their side. That covers the common use case where applications have a single control thread or otherwise perform locking on their own.

Quoted 4 lines

Once the basics are there for most PMDs, we may add new functions, items, properties and actions that provide additional constraints (timing, multi-threading and so on), which remain to be defined according to feedback. It is designed to be extended without causing ABI breakage.

I think Sugesh and I are trying to foresee some of the issues that may arise when integrating with something like OVS. OTOH it's hard/impossible to say what will be needed exactly in the API right now to make it suitable for OVS.

So, I'm ok with the approach you are taking by exposing a basic API but I think there should be an expectation that it may not be sufficient for a project like OVS to integrate in and may take several iterations/extensions - don't go anywhere!

Quoted 5 lines

As for query(), let's see how PMDs handle it first. A race between query() and create() on a given device is almost unavoidable without locking, same for queries that reset counters in a given flow rule. Basic parallel queries should not cause any harm otherwise, although this cannot be guaranteed yet.

You still have a race if there is locking, except it is for the lock, but it has the same effect. The downside of my suggestion is that all the PMDs would need to guarantee they could gets stats atomically - I'm not sure if they can or it's too restrictive.

Quoted 1 line

<...>

Quoted 21 lines
Quoted 21 lines
Quoted 20 lines
Quoted 13 lines

lib/librte_ether/rte_flow.h line 879

Quoted 11 lines
lib/librte_ether/rte_flow.h new file +9 -0
+
+/**
+ * Destroy a flow rule on a given port.
+ *
+ * Failure to destroy a flow rule handle may occur when other flow rules
+ * depend on it, and destroying it would result in an inconsistent state.
+ *
+ * This function is only guaranteed to succeed if handles are destroyed in
+ * reverse order of their creation.

How can the application find this information out on error?

Without maintaining a list, they cannot. The specified case is the only possible guarantee. That does not mean PMDs should not do their best to destroy flow rules, only that ordering must remain consistent in case of inability to destroy one.

What do you suggest?

Quoted 7 lines
Quoted 3 lines

I think if the app cannot remove a specific rule it may want to remove all rules and deal with flows in software for a time. So once the app knows it fails that should be enough.

OK, then since destruction may return an error already, is it fine? Applications may call rte_flow_flush() (not supposed to fail unless there is a serious issue, abort() in that case) and switch to SW fallback.

yes, it's fine.

Quoted 1 line

<...>

Quoted 37 lines
Quoted 29 lines
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines

+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.h new file +6 -0
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       struct rte_flow_error *error);
Quoted 8 lines

lib/librte_ether/rte_flow.h line 945

Quoted 4 lines
lib/librte_ether/rte_flow.h new file +4 -0
+
+#ifdef __cplusplus
+}
+#endif

I don't see a way to dump all the rules for a port out. I think this is neccessary for degbugging. You could have a look through dpif.h in OVS and see how dpif_flow_dump_next() is used, it might be a good reference.

Quoted 4 lines

DPDK does not maintain flow rules and, depending on hardware capabilities and level of compliance, PMDs do not necessarily do it either, particularly since it requires space and application probably have a better method to store these pointers for their own needs.

understood

Quoted 1 line
Quoted 7 lines
Quoted 3 lines

What you see here is only a PMD interface. Depending on applications needs, generic helper functions built on top of these may be added to manage flow rules in the future.

I'm thinking of the case where something goes wrong and I want to get a dump of all the flow rules from hardware, not query the rules I think I have. I don't see a way to do it or something to build a helper on top of?

Quoted 8 lines

Generic helper functions would exist on top of this API and would likely maintain a list of flow rules themselves. The dump in that case would be entirely implemented in software. I think that recovering flow rules from HW may be complicated in many cases (even without taking storage allocation and rules conversion issues into account), therefore if there is really a need for it, we could perhaps add a dump() function that PMDs are free to implement later.

ok. Maybe there are some more generic stats that can be got from the hardware that would help debugging that would suffice, like total flow rule hits/misses (i.e. not on a per flow rule basis).

You can get this from the software flow caches and it's widely used for debugging. e.g.

pmd thread numa_id 0 core_id 3:

	emc hits:0
	megaflow hits:0
	avg. subtable lookups per hit:0.00
	miss:0
#77 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Kevin,

Quoted 142 lines · Kevin Traynor

hi Adrien, sorry for the delay

<...>

Quoted 23 lines
Quoted 11 lines
Quoted 10 lines
Quoted 3 lines

Is it expected that the application or pmd will provide locking between these functions if required? I think it's going to have to be the app.

Locking is indeed expected to be performed by applications. This API only documents places where locking would make sense if necessary and expected behavior.

Like all control path APIs, this one assumes a single control thread. Applications must take the necessary precautions.

Quoted 6 lines

If you look at OVS now it's quite possible that you have 2 rx queues serviced by different threads, that would also install the flow rules in the software flow caches - possibly that could extend to adding hardware flows. There could also be another thread that is querying for stats. So anything that can be done to minimise the locking would be helpful - maybe query() could be atomic and not require any locking?

I think we need basic functions with as few constraints as possible on PMDs first, this API being somewhat complex to implement on their side. That covers the common use case where applications have a single control thread or otherwise perform locking on their own.

Quoted 4 lines

Once the basics are there for most PMDs, we may add new functions, items, properties and actions that provide additional constraints (timing, multi-threading and so on), which remain to be defined according to feedback. It is designed to be extended without causing ABI breakage.

I think Sugesh and I are trying to foresee some of the issues that may arise when integrating with something like OVS. OTOH it's hard/impossible to say what will be needed exactly in the API right now to make it suitable for OVS.

So, I'm ok with the approach you are taking by exposing a basic API but I think there should be an expectation that it may not be sufficient for a project like OVS to integrate in and may take several iterations/extensions - don't go anywhere!

Quoted 5 lines

As for query(), let's see how PMDs handle it first. A race between query() and create() on a given device is almost unavoidable without locking, same for queries that reset counters in a given flow rule. Basic parallel queries should not cause any harm otherwise, although this cannot be guaranteed yet.

You still have a race if there is locking, except it is for the lock, but it has the same effect. The downside of my suggestion is that all the PMDs would need to guarantee they could gets stats atomically - I'm not sure if they can or it's too restrictive.

Quoted 1 line

<...>

Quoted 21 lines
Quoted 21 lines
Quoted 20 lines
Quoted 13 lines

lib/librte_ether/rte_flow.h line 879

Quoted 11 lines
lib/librte_ether/rte_flow.h new file +9 -0
+
+/**
+ * Destroy a flow rule on a given port.
+ *
+ * Failure to destroy a flow rule handle may occur when other flow rules
+ * depend on it, and destroying it would result in an inconsistent state.
+ *
+ * This function is only guaranteed to succeed if handles are destroyed in
+ * reverse order of their creation.

How can the application find this information out on error?

Without maintaining a list, they cannot. The specified case is the only possible guarantee. That does not mean PMDs should not do their best to destroy flow rules, only that ordering must remain consistent in case of inability to destroy one.

What do you suggest?

Quoted 7 lines
Quoted 3 lines

I think if the app cannot remove a specific rule it may want to remove all rules and deal with flows in software for a time. So once the app knows it fails that should be enough.

OK, then since destruction may return an error already, is it fine? Applications may call rte_flow_flush() (not supposed to fail unless there is a serious issue, abort() in that case) and switch to SW fallback.

yes, it's fine.

Quoted 1 line

<...>

Quoted 37 lines
Quoted 29 lines
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines

+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

lib/librte_ether/rte_flow.h new file +6 -0
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       struct rte_flow_error *error);
Quoted 8 lines

lib/librte_ether/rte_flow.h line 945

Quoted 4 lines
lib/librte_ether/rte_flow.h new file +4 -0
+
+#ifdef __cplusplus
+}
+#endif

I don't see a way to dump all the rules for a port out. I think this is neccessary for degbugging. You could have a look through dpif.h in OVS and see how dpif_flow_dump_next() is used, it might be a good reference.

Quoted 4 lines

DPDK does not maintain flow rules and, depending on hardware capabilities and level of compliance, PMDs do not necessarily do it either, particularly since it requires space and application probably have a better method to store these pointers for their own needs.

understood

Quoted 1 line
Quoted 7 lines
Quoted 3 lines

What you see here is only a PMD interface. Depending on applications needs, generic helper functions built on top of these may be added to manage flow rules in the future.

I'm thinking of the case where something goes wrong and I want to get a dump of all the flow rules from hardware, not query the rules I think I have. I don't see a way to do it or something to build a helper on top of?

Quoted 8 lines

Generic helper functions would exist on top of this API and would likely maintain a list of flow rules themselves. The dump in that case would be entirely implemented in software. I think that recovering flow rules from HW may be complicated in many cases (even without taking storage allocation and rules conversion issues into account), therefore if there is really a need for it, we could perhaps add a dump() function that PMDs are free to implement later.

ok. Maybe there are some more generic stats that can be got from the hardware that would help debugging that would suffice, like total flow rule hits/misses (i.e. not on a per flow rule basis).

Quoted 9 lines

You can get this from the software flow caches and it's widely used for debugging. e.g.

pmd thread numa_id 0 core_id 3:

	emc hits:0
	megaflow hits:0
	avg. subtable lookups per hit:0.00
	miss:0

Perhaps a rule such as the following could do the trick:

group: 42 (or priority 42)
pattern: void
actions: count / passthru

Assuming useful flow rules are defined with higher priorities (using lower group ID or priority level) and provide a terminating action, this one would count all packets that were not caught by them.

That is one example to illustrate how "global" counters can be requested by applications.

Otherwise you could just make sure all rules contain mark / flag actions, in which case mbufs would tell directly if they went through them or need additional SW processing.

Signature · 2 lines
Adrien Mazarguil
6WIND
#78 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Quoted 71 lines · Adrien Mazarguil
Quoted 51 lines
Quoted 38 lines
Quoted 30 lines
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines

+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */

(quoted diff) +6 -0
+int
+rte_flow_query(uint8_t port_id,
+	       struct rte_flow *flow,
+	       enum rte_flow_action_type action,
+	       void *data,
+	       struct rte_flow_error *error);
Quoted 8 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+
+#ifdef __cplusplus
+}
+#endif

I don't see a way to dump all the rules for a port out. I think this is neccessary for degbugging. You could have a look through dpif.h in OVS and see how dpif_flow_dump_next() is used, it might be a good reference.

Quoted 4 lines

DPDK does not maintain flow rules and, depending on hardware capabilities and level of compliance, PMDs do not necessarily do it either, particularly since it requires space and application probably have a better method to store these pointers for their own needs.

understood

Quoted 1 line
Quoted 7 lines
Quoted 3 lines

What you see here is only a PMD interface. Depending on applications needs, generic helper functions built on top of these may be added to manage flow rules in the future.

I'm thinking of the case where something goes wrong and I want to get a dump of all the flow rules from hardware, not query the rules I think I have. I don't see a way to do it or something to build a helper on top of?

Quoted 8 lines

Generic helper functions would exist on top of this API and would likely maintain a list of flow rules themselves. The dump in that case would be entirely implemented in software. I think that recovering flow rules from HW may be complicated in many cases (even without taking storage allocation and rules conversion issues into account), therefore if there is really a need for it, we could perhaps add a dump() function that PMDs are free to implement later.

ok. Maybe there are some more generic stats that can be got from the hardware that would help debugging that would suffice, like total flow rule hits/misses (i.e. not on a per flow rule basis).

Quoted 9 lines

You can get this from the software flow caches and it's widely used for debugging. e.g.

pmd thread numa_id 0 core_id 3:

	emc hits:0
	megaflow hits:0
	avg. subtable lookups per hit:0.00
	miss:0

Perhaps a rule such as the following could do the trick:

group: 42 (or priority 42)
pattern: void
actions: count / passthru

Assuming useful flow rules are defined with higher priorities (using lower group ID or priority level) and provide a terminating action, this one would count all packets that were not caught by them.

Quoted 7 lines

That is one example to illustrate how "global" counters can be requested by applications.

Otherwise you could just make sure all rules contain mark / flag actions, in which case mbufs would tell directly if they went through them or need additional SW processing.

ok, sounds like there's some options at least to work with on this which is good. thanks.

#79 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

In reply to Adrien Mazarguil
Quoted 41 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Thursday, November 17, 2016 12:23 AM
To: [email protected]
Cc: Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>
Subject: [dpdk-dev] [PATCH 01/22] ethdev: introduce generic flow API

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

---
 MAINTAINERS                            |   4 +
 lib/librte_ether/Makefile              |   3 +
 lib/librte_ether/rte_eth_ctrl.h        |   1 +
 lib/librte_ether/rte_ether_version.map |  10 +
 lib/librte_ether/rte_flow.c            | 159 +++++
 lib/librte_ether/rte_flow.h            | 947 ++++++++++++++++++++++++++++
 lib/librte_ether/rte_flow_driver.h     | 177 ++++++
 7 files changed, 1301 insertions(+)

lib/librte_ether/rte_flow.h line 377

Quoted 9 lines
lib/librte_ether/rte_flow.h new file +9 -0
+/**
+ * RTE_FLOW_ITEM_TYPE_ETH
+ *
+ * Matches an Ethernet header.
+ */
+struct rte_flow_item_eth {
+	struct ether_addr dst; /**< Destination MAC. */
+	struct ether_addr src; /**< Source MAC. */
+	unsigned int type; /**< EtherType. */

Hi Adrien,

ETHERTYPE in ether header is 2 bytes, so I think "uint16_t type" is more appropriate here, what do you think?

Thanks,
Beilei Xing

Quoted 2 lines
(quoted diff) +2 -0
+};
+
#80 lore raw

Re: [PATCH 01/22] ethdev: introduce generic flow API

Hi Beilei,

On Thu, Dec 08, 2016 at 09:00:05AM +0000, Xing, Beilei wrote:
[...]

Quoted 11 lines

lib/librte_ether/rte_flow.h line 377

Quoted 9 lines
lib/librte_ether/rte_flow.h new file +9 -0
+/**
+ * RTE_FLOW_ITEM_TYPE_ETH
+ *
+ * Matches an Ethernet header.
+ */
+struct rte_flow_item_eth {
+	struct ether_addr dst; /**< Destination MAC. */
+	struct ether_addr src; /**< Source MAC. */
+	unsigned int type; /**< EtherType. */

Hi Adrien,

Quoted 1 line

ETHERTYPE in ether header is 2 bytes, so I think "uint16_t type" is more appropriate here, what do you think?

You're right, thanks for catching this. I'll update it in v2 (soon).

Signature · 2 lines
Adrien Mazarguil
6WIND
#81 lore raw

PATCH 2/22 cmdline: add support for dynamic tokens

In reply to Adrien Mazarguil

Considering tokens must be hard-coded in a list part of the instruction structure, context-dependent tokens cannot be expressed.

This commit adds support for building dynamic token lists through a user-provided function, which is called when the static token list is empty (a single NULL entry).

Because no structures are modified (existing fields are reused), this commit has no impact on the current ABI.

---
 lib/librte_cmdline/cmdline_parse.c | 60 +++++++++++++++++++++++++++++----
 lib/librte_cmdline/cmdline_parse.h | 21 ++++++++++++
 2 files changed, 74 insertions(+), 7 deletions(-)
lib/librte_cmdline/cmdline_parse.c +53 -7
index b496067..14f5553 100644
@@ -146,7 +146,9 @@ nb_common_chars(const char * s1, const char * s2)
  */
 static int
 match_inst(cmdline_parse_inst_t *inst, const char *buf,
-	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
+	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size,
+	   cmdline_parse_token_hdr_t
+		*(*dyn_tokens)[CMDLINE_PARSE_DYNAMIC_TOKENS])
 {
 	unsigned int token_num=0;
 	cmdline_parse_token_hdr_t * token_p;
@@ -155,6 +157,11 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf,
 	struct cmdline_token_hdr token_hdr;
 
 	token_p = inst->tokens[token_num];
+	if (!token_p && dyn_tokens && inst->f) {
+		if (!(*dyn_tokens)[0])
+			inst->f(&(*dyn_tokens)[0], NULL, dyn_tokens);
+		token_p = (*dyn_tokens)[0];
+	}
 	if (token_p)
 		memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
@@ -196,7 +203,17 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf,
 		buf += n;
 
 		token_num ++;
-		token_p = inst->tokens[token_num];
+		if (!inst->tokens[0]) {
+			if (token_num < (CMDLINE_PARSE_DYNAMIC_TOKENS - 1)) {
+				if (!(*dyn_tokens)[token_num])
+					inst->f(&(*dyn_tokens)[token_num],
+						NULL,
+						dyn_tokens);
+				token_p = (*dyn_tokens)[token_num];
+			} else
+				token_p = NULL;
+		} else
+			token_p = inst->tokens[token_num];
 		if (token_p)
 			memcpy(&token_hdr, token_p, sizeof(token_hdr));
 	}
@@ -239,6 +256,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 	cmdline_parse_inst_t *inst;
 	const char *curbuf;
 	char result_buf[CMDLINE_PARSE_RESULT_BUFSIZE];
+	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	void (*f)(void *, struct cmdline *, void *) = NULL;
 	void *data = NULL;
 	int comment = 0;
@@ -255,6 +273,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 		return CMDLINE_PARSE_BAD_ARGS;
 
 	ctx = cl->ctx;
+	memset(&dyn_tokens, 0, sizeof(dyn_tokens));
 
 	/*
 	 * - look if the buffer contains at least one line
@@ -299,7 +318,8 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 		debug_printf("INST %d\n", inst_num);
 
 		/* fully parsed */
-		tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf));
+		tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf),
+				 &dyn_tokens);
 
 		if (tok > 0) /* we matched at least one token */
 			err = CMDLINE_PARSE_BAD_ARGS;
@@ -355,6 +375,7 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 	cmdline_parse_token_hdr_t *token_p;
 	struct cmdline_token_hdr token_hdr;
 	char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
+	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	unsigned int partial_tok_len;
 	int comp_len = -1;
 	int tmp_len = -1;
@@ -374,6 +395,7 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 
 	debug_printf("%s called\n", __func__);
 	memset(&token_hdr, 0, sizeof(token_hdr));
+	memset(&dyn_tokens, 0, sizeof(dyn_tokens));
 
 	/* count the number of complete token to parse */
 	for (i=0 ; buf[i] ; i++) {
@@ -396,11 +418,24 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 		inst = ctx[inst_num];
 		while (inst) {
 			/* parse the first tokens of the inst */
-			if (nb_token && match_inst(inst, buf, nb_token, NULL, 0))
+			if (nb_token &&
+			    match_inst(inst, buf, nb_token, NULL, 0,
+				       &dyn_tokens))
 				goto next;
 
 			debug_printf("instruction match\n");
-			token_p = inst->tokens[nb_token];
+			if (!inst->tokens[0]) {
+				if (nb_token <
+				    (CMDLINE_PARSE_DYNAMIC_TOKENS - 1)) {
+					if (!dyn_tokens[nb_token])
+						inst->f(&dyn_tokens[nb_token],
+							NULL,
+							&dyn_tokens);
+					token_p = dyn_tokens[nb_token];
+				} else
+					token_p = NULL;
+			} else
+				token_p = inst->tokens[nb_token];
 			if (token_p)
 				memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
@@ -490,10 +525,21 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 		/* we need to redo it */
 		inst = ctx[inst_num];
 
-		if (nb_token && match_inst(inst, buf, nb_token, NULL, 0))
+		if (nb_token &&
+		    match_inst(inst, buf, nb_token, NULL, 0, &dyn_tokens))
 			goto next2;
 
-		token_p = inst->tokens[nb_token];
+		if (!inst->tokens[0]) {
+			if (nb_token < (CMDLINE_PARSE_DYNAMIC_TOKENS - 1)) {
+				if (!dyn_tokens[nb_token])
+					inst->f(&dyn_tokens[nb_token],
+						NULL,
+						&dyn_tokens);
+				token_p = dyn_tokens[nb_token];
+			} else
+				token_p = NULL;
+		} else
+			token_p = inst->tokens[nb_token];
 		if (token_p)
 			memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
lib/librte_cmdline/cmdline_parse.h +21 -0
index 4ac05d6..65b18d4 100644
@@ -83,6 +83,9 @@ extern "C" {
 /* maximum buffer size for parsed result */
 #define CMDLINE_PARSE_RESULT_BUFSIZE 8192
 
+/* maximum number of dynamic tokens */
+#define CMDLINE_PARSE_DYNAMIC_TOKENS 128
+
 /**
  * Stores a pointer to the ops struct, and the offset: the place to
  * write the parsed result in the destination structure.
@@ -130,6 +133,24 @@ struct cmdline;
  * Store a instruction, which is a pointer to a callback function and
  * its parameter that is called when the instruction is parsed, a help
  * string, and a list of token composing this instruction.
+ *
+ * When no tokens are defined (tokens[0] == NULL), they are retrieved
+ * dynamically by calling f() as follows:
+ *
+ *  f((struct cmdline_token_hdr **)&token_hdr,
+ *    NULL,
+ *    (struct cmdline_token_hdr *[])tokens));
+ *
+ * The address of the resulting token is expected at the location pointed by
+ * the first argument. Can be set to NULL to end the list.
+ *
+ * The cmdline argument (struct cmdline *) is always NULL.
+ *
+ * The last argument points to the NULL-terminated list of dynamic tokens
+ * defined so far. Since token_hdr points to an index of that list, the
+ * current index can be derived as follows:
+ *
+ *  int index = token_hdr - &(*tokens)[0];
  */
 struct cmdline_inst {
 	/* f(parsed_struct, data) */
Signature · 1 line
2.1.4
#82 lore raw

PATCH 3/22 cmdline: add alignment constraint

In reply to Adrien Mazarguil

This prevents sigbus errors on architectures that cannot handle unexpected unaligned accesses to the output buffer.

---
 lib/librte_cmdline/cmdline_parse.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
lib/librte_cmdline/cmdline_parse.c +6 -3
index 14f5553..763c286 100644
@@ -255,7 +255,10 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 	unsigned int inst_num=0;
 	cmdline_parse_inst_t *inst;
 	const char *curbuf;
-	char result_buf[CMDLINE_PARSE_RESULT_BUFSIZE];
+	union {
+		char buf[CMDLINE_PARSE_RESULT_BUFSIZE];
+		long double align; /* strong alignment constraint for buf */
+	} result;
 	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	void (*f)(void *, struct cmdline *, void *) = NULL;
 	void *data = NULL;
@@ -318,7 +321,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 		debug_printf("INST %d\n", inst_num);
 
 		/* fully parsed */
-		tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf),
+		tok = match_inst(inst, buf, 0, result.buf, sizeof(result.buf),
 				 &dyn_tokens);
 
 		if (tok > 0) /* we matched at least one token */
@@ -353,7 +356,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 
 	/* call func */
 	if (f) {
-		f(result_buf, cl, data);
+		f(result.buf, cl, data);
 	}
 
 	/* no match */
Signature · 1 line
2.1.4
#83 lore raw

PATCH 4/22 app/testpmd: implement basic support for rte_flow

In reply to Adrien Mazarguil

Add basic management functions for the generic flow API (validate, create, destroy, flush, query and list). Flow rule objects and properties are arranged in lists associated with each port.

---
 app/test-pmd/cmdline.c     |   1 +
 app/test-pmd/config.c      | 484 ++++++++++++++++++++++++++++++++++++++++
 app/test-pmd/csumonly.c    |   1 +
 app/test-pmd/flowgen.c     |   1 +
 app/test-pmd/icmpecho.c    |   1 +
 app/test-pmd/ieee1588fwd.c |   1 +
 app/test-pmd/iofwd.c       |   1 +
 app/test-pmd/macfwd.c      |   1 +
 app/test-pmd/macswap.c     |   1 +
 app/test-pmd/parameters.c  |   1 +
 app/test-pmd/rxonly.c      |   1 +
 app/test-pmd/testpmd.c     |   6 +
 app/test-pmd/testpmd.h     |  27 +++
 app/test-pmd/txonly.c      |   1 +
 14 files changed, 528 insertions(+)
app/test-pmd/cmdline.c +1 -0
index 63b55dc..c5b015c 100644
@@ -75,6 +75,7 @@
 #include <rte_string_fns.h>
 #include <rte_devargs.h>
 #include <rte_eth_ctrl.h>
+#include <rte_flow.h>
 
 #include <cmdline_rdline.h>
 #include <cmdline_parse.h>
app/test-pmd/config.c +484 -0

496 lines not on this page · raw message

app/test-pmd/csumonly.c +1 -0
index 57e6ae2..dd67ebf 100644
@@ -70,6 +70,7 @@
 #include <rte_sctp.h>
 #include <rte_prefetch.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 #include "testpmd.h"
 
 #define IP_DEFTTL  64   /* from RFC 1340. */
app/test-pmd/flowgen.c +1 -0
index b13ff89..13b4f90 100644
@@ -68,6 +68,7 @@
 #include <rte_tcp.h>
 #include <rte_udp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/icmpecho.c +1 -0
index 6a4e750..f25a8f5 100644
@@ -61,6 +61,7 @@
 #include <rte_ip.h>
 #include <rte_icmp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/ieee1588fwd.c +1 -0
index 0d3b37a..51170ee 100644
@@ -34,6 +34,7 @@
 
 #include <rte_cycles.h>
 #include <rte_ethdev.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/iofwd.c +1 -0
index 26936b7..15cb4a2 100644
@@ -64,6 +64,7 @@
 #include <rte_ether.h>
 #include <rte_ethdev.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/macfwd.c +1 -0
index 86e01de..d361db1 100644
@@ -65,6 +65,7 @@
 #include <rte_ethdev.h>
 #include <rte_ip.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/macswap.c +1 -0
index 36e139f..f996039 100644
@@ -65,6 +65,7 @@
 #include <rte_ethdev.h>
 #include <rte_ip.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/parameters.c +1 -0
index 08e5a76..28db8cd 100644
@@ -76,6 +76,7 @@
 #ifdef RTE_LIBRTE_PMD_BOND
 #include <rte_eth_bond.h>
 #endif
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/rxonly.c +1 -0
index fff815c..cf00576 100644
@@ -67,6 +67,7 @@
 #include <rte_ip.h>
 #include <rte_udp.h>
 #include <rte_net.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/testpmd.c +6 -0
index a0332c2..bfb2f8e 100644
@@ -78,6 +78,7 @@
 #ifdef RTE_LIBRTE_PDUMP
 #include <rte_pdump.h>
 #endif
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
@@ -1545,6 +1546,8 @@ close_port(portid_t pid)
 			continue;
 		}
 
+		if (port->flow_list)
+			port_flow_flush(pi);
 		rte_eth_dev_close(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
@@ -1599,6 +1602,9 @@ detach_port(uint8_t port_id)
 		return;
 	}
 
+	if (ports[port_id].flow_list)
+		port_flow_flush(port_id);
+
 	if (rte_eth_dev_detach(port_id, name))
 		return;
 
app/test-pmd/testpmd.h +27 -0
index 9c1e703..22ce2d6 100644
@@ -144,6 +144,19 @@ struct fwd_stream {
 /** Insert double VLAN header in forward engine */
 #define TESTPMD_TX_OFFLOAD_INSERT_QINQ       0x0080
 
+/** Descriptor for a single flow. */
+struct port_flow {
+	size_t size; /**< Allocated space including data[]. */
+	struct port_flow *next; /**< Next flow in list. */
+	struct port_flow *tmp; /**< Temporary linking. */
+	uint32_t id; /**< Flow rule ID. */
+	struct rte_flow *flow; /**< Opaque flow object returned by PMD. */
+	struct rte_flow_attr attr; /**< Attributes. */
+	struct rte_flow_item *pattern; /**< Pattern. */
+	struct rte_flow_action *actions; /**< Actions. */
+	uint8_t data[]; /**< Storage for pattern/actions. */
+};
+
 /**
  * The data structure associated with each port.
  */
@@ -177,6 +190,7 @@ struct rte_port {
 	struct ether_addr       *mc_addr_pool; /**< pool of multicast addrs */
 	uint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */
 	uint8_t                 slave_flag; /**< bonding slave port */
+	struct port_flow        *flow_list; /**< Associated flows. */
 };
 
 extern portid_t __rte_unused
@@ -504,6 +518,19 @@ void port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,
 			    uint8_t bit1_pos, uint8_t bit2_pos, uint32_t value);
 void port_reg_display(portid_t port_id, uint32_t reg_off);
 void port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t value);
+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);
+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);
+int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
+int port_flow_flush(portid_t port_id);
+int port_flow_query(portid_t port_id, uint32_t rule,
+		    enum rte_flow_action_type action);
+void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
 
 void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id);
 void tx_ring_desc_display(portid_t port_id, queueid_t txq_id, uint16_t txd_id);
app/test-pmd/txonly.c +1 -0
index 8513a06..e996f35 100644
@@ -68,6 +68,7 @@
 #include <rte_tcp.h>
 #include <rte_udp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
Signature · 1 line
2.1.4
#84 lore raw

PATCH 5/22 app/testpmd: add flow command

In reply to Adrien Mazarguil

Managing generic flow API functions from command line requires the use of dynamic tokens for convenience as flow rules are not fixed and cannot be defined statically.

This commit adds specific flexible parser code and object for a new "flow" command in separate file.

---
 app/test-pmd/Makefile       |   1 +
 app/test-pmd/cmdline.c      |   4 +
 app/test-pmd/cmdline_flow.c | 439 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 444 insertions(+)
app/test-pmd/Makefile +1 -0
index 891b85a..5988c3e 100644
@@ -47,6 +47,7 @@ CFLAGS += $(WERROR_FLAGS)
 SRCS-y := testpmd.c
 SRCS-y += parameters.c
 SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline.c
+SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline_flow.c
 SRCS-y += config.c
 SRCS-y += iofwd.c
 SRCS-y += macfwd.c
app/test-pmd/cmdline.c +4 -0
index c5b015c..b7d10b3 100644
@@ -9520,6 +9520,9 @@ cmdline_parse_inst_t cmd_set_flow_director_flex_payload = {
 	},
 };
 
+/* Generic flow interface command. */
+extern cmdline_parse_inst_t cmd_flow;
+
 /* *** Classification Filters Control *** */
 /* *** Get symmetric hash enable per port *** */
 struct cmd_get_sym_hash_ena_per_port_result {
@@ -11557,6 +11560,7 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_set_hash_global_config,
 	(cmdline_parse_inst_t *)&cmd_set_hash_input_set,
 	(cmdline_parse_inst_t *)&cmd_set_fdir_input_set,
+	(cmdline_parse_inst_t *)&cmd_flow,
 	(cmdline_parse_inst_t *)&cmd_mcast_addr,
 	(cmdline_parse_inst_t *)&cmd_config_l2_tunnel_eth_type_all,
 	(cmdline_parse_inst_t *)&cmd_config_l2_tunnel_eth_type_specific,
app/test-pmd/cmdline_flow.c new file +439 -0

439 lines not on this page · raw message

Signature · 1 line
2.1.4
#85 lore raw

PATCH 6/22 app/testpmd: add rte_flow integer support

In reply to Adrien Mazarguil

Parse all integer types and handle conversion to network byte order in a single function.

---
 app/test-pmd/cmdline_flow.c | 148 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)
app/test-pmd/cmdline_flow.c +148 -0
index 7dbda84..7078f80 100644
@@ -34,11 +34,14 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
 #include <ctype.h>
 #include <string.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_byteorder.h>
 #include <cmdline_parse.h>
 #include <rte_flow.h>
 
@@ -50,6 +53,10 @@ enum index {
 	ZERO = 0,
 	END,
 
+	/* Common tokens. */
+	INTEGER,
+	UNSIGNED,
+
 	/* Top-level command. */
 	FLOW,
 };
@@ -61,12 +68,24 @@ enum index {
 struct context {
 	/** Stack of subsequent token lists to process. */
 	const enum index *next[CTX_STACK_SIZE];
+	/** Arguments for stacked tokens. */
+	const void *args[CTX_STACK_SIZE];
 	enum index curr; /**< Current token index. */
 	enum index prev; /**< Index of the last token seen. */
 	int next_num; /**< Number of entries in next[]. */
+	int args_num; /**< Number of entries in args[]. */
 	uint32_t reparse:1; /**< Start over from the beginning. */
 	uint32_t eol:1; /**< EOL has been detected. */
 	uint32_t last:1; /**< No more arguments. */
+	void *object; /**< Address of current object for relative offsets. */
+};
+
+/** Token argument. */
+struct arg {
+	uint32_t hton:1; /**< Use network byte ordering. */
+	uint32_t sign:1; /**< Value is signed. */
+	uint32_t offset; /**< Relative offset from ctx->object. */
+	uint32_t size; /**< Field size. */
 };
 
 /** Parser token definition. */
@@ -80,6 +99,8 @@ struct token {
 	 * parser consumes the last entry of that stack.
 	 */
 	const enum index *const *next;
+	/** Arguments stack for subsequent tokens that need them. */
+	const struct arg *const *args;
 	/**
 	 * Token-processing callback, returns -1 in case of error, the
 	 * length of the matched string otherwise. If NULL, attempts to
@@ -112,6 +133,22 @@ struct token {
 /** Static initializer for a NEXT() entry. */
 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, 0, }
 
+/** Static initializer for the args field. */
+#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
+
+/** Static initializer for ARGS() to target a field. */
+#define ARGS_ENTRY(s, f) \
+	(&(const struct arg){ \
+		.offset = offsetof(s, f), \
+		.size = sizeof(((s *)0)->f), \
+	})
+
+/** Static initializer for ARGS() to target a pointer. */
+#define ARGS_ENTRY_PTR(s, f) \
+	(&(const struct arg){ \
+		.size = sizeof(*((s *)0)->f), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -121,6 +158,11 @@ struct buffer {
 static int parse_init(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);
+static int comp_none(struct context *, const struct token *,
+		     unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -135,6 +177,21 @@ static const struct token token_list[] = {
 		.type = "RETURN",
 		.help = "command may end here",
 	},
+	/* Common tokens. */
+	[INTEGER] = {
+		.name = "{int}",
+		.type = "INTEGER",
+		.help = "integer value",
+		.call = parse_int,
+		.comp = comp_none,
+	},
+	[UNSIGNED] = {
+		.name = "{unsigned}",
+		.type = "UNSIGNED",
+		.help = "unsigned integer value",
+		.call = parse_int,
+		.comp = comp_none,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
@@ -144,6 +201,23 @@ static const struct token token_list[] = {
 	},
 };
 
+/** Remove and return last entry from argument stack. */
+static const struct arg *
+pop_args(struct context *ctx)
+{
+	return ctx->args_num ? ctx->args[--ctx->args_num] : NULL;
+}
+
+/** Add entry on top of the argument stack. */
+static int
+push_args(struct context *ctx, const struct arg *arg)
+{
+	if (ctx->args_num == CTX_STACK_SIZE)
+		return -1;
+	ctx->args[ctx->args_num++] = arg;
+	return 0;
+}
+
 /** Default parsing function for token name matching. */
 static int
 parse_default(struct context *ctx, const struct token *token,
@@ -178,9 +252,74 @@ parse_init(struct context *ctx, const struct token *token,
 	/* Initialize buffer. */
 	memset(out, 0x00, sizeof(*out));
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
+	ctx->object = out;
 	return len;
 }
 
+/**
+ * Parse signed/unsigned integers 8 to 64-bit long.
+ *
+ * Last argument (ctx->args) is retrieved to determine integer type and
+ * storage location.
+ */
+static int
+parse_int(struct context *ctx, const struct token *token,
+	  const char *str, unsigned int len,
+	  void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	uintmax_t u;
+	char *end;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	errno = 0;
+	u = arg->sign ?
+		(uintmax_t)strtoimax(str, &end, 0) :
+		strtoumax(str, &end, 0);
+	if (errno || (size_t)(end - str) != len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	size = arg->size;
+	switch (size) {
+	case sizeof(uint8_t):
+		*(uint8_t *)buf = u;
+		break;
+	case sizeof(uint16_t):
+		*(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
+		break;
+	case sizeof(uint32_t):
+		*(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
+		break;
+	case sizeof(uint64_t):
+		*(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
+		break;
+	default:
+		goto error;
+	}
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
+/** No completion. */
+static int
+comp_none(struct context *ctx, const struct token *token,
+	  unsigned int ent, char *buf, unsigned int size)
+{
+	(void)ctx;
+	(void)token;
+	(void)ent;
+	(void)buf;
+	(void)size;
+	return 0;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -195,9 +334,11 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->curr = 0;
 	ctx->prev = 0;
 	ctx->next_num = 0;
+	ctx->args_num = 0;
 	ctx->reparse = 0;
 	ctx->eol = 0;
 	ctx->last = 0;
+	ctx->object = NULL;
 }
 
 /** Parse a token (cmdline API). */
@@ -270,6 +411,13 @@ cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
 				return -1;
 			ctx->next[ctx->next_num++] = token->next[i];
 		}
+	/* Push arguments if any. */
+	if (token->args)
+		for (i = 0; token->args[i]; ++i) {
+			if (ctx->args_num == RTE_DIM(ctx->args))
+				return -1;
+			ctx->args[ctx->args_num++] = token->args[i];
+		}
 	return len;
 }
 
Signature · 1 line
2.1.4
#86 lore raw

PATCH 7/22 app/testpmd: add flow list command

In reply to Adrien Mazarguil

Syntax:

flow list {port_id} [group {group_id}] [...]

List configured flow rules on a port. Output can optionally be limited to a given set of group identifiers.

---
 app/test-pmd/cmdline.c      |   4 ++
 app/test-pmd/cmdline_flow.c | 141 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 145 insertions(+)
app/test-pmd/cmdline.c +4 -0
index b7d10b3..09357c0 100644
@@ -810,6 +810,10 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"sctp-src-port|sctp-dst-port|sctp-veri-tag|none)"
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
+
+			"flow list {port_id} [group {group_id}] [...]\n"
+			"    List existing flow rules sorted by priority,"
+			" filtered by group identifiers.\n\n"
 		);
 	}
 }
app/test-pmd/cmdline_flow.c +141 -0

215 lines not on this page · raw message

Signature · 1 line
2.1.4
#87 lore raw

PATCH 8/22 app/testpmd: add flow flush command

In reply to Adrien Mazarguil

Syntax:

flow flush {port_id}

Destroy all flow rules on a port.

---
 app/test-pmd/cmdline.c      |  3 +++
 app/test-pmd/cmdline_flow.c | 43 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +3 -0
index 09357c0..9f124fc 100644
@@ -811,6 +811,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow flush {port_id}\n"
+			"    Destroy all flow rules.\n\n"
+
 			"flow list {port_id} [group {group_id}] [...]\n"
 			"    List existing flow rules sorted by priority,"
 			" filtered by group identifiers.\n\n"
app/test-pmd/cmdline_flow.c +42 -1

76 lines not on this page · raw message

Signature · 1 line
2.1.4
#88 lore raw

PATCH 9/22 app/testpmd: add flow destroy command

In reply to Adrien Mazarguil

Syntax:

flow destroy {port_id} rule {rule_id} [...]

Destroy a given set of flow rules associated with a port.

---
 app/test-pmd/cmdline.c      |   3 ++
 app/test-pmd/cmdline_flow.c | 106 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 108 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +3 -0
index 9f124fc..20a64b6 100644
@@ -811,6 +811,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow destroy {port_id} rule {rule_id} [...]\n"
+			"    Destroy specific flow rules.\n\n"
+
 			"flow flush {port_id}\n"
 			"    Destroy all flow rules.\n\n"
 
app/test-pmd/cmdline_flow.c +105 -1

185 lines not on this page · raw message

Signature · 1 line
2.1.4
#89 lore raw

PATCH 10/22 app/testpmd: add flow validate/create commands

In reply to Adrien Mazarguil

Syntax:

flow (validate|create) {port_id}

    [group {group_id}] [priority {level}] [ingress] [egress]
    pattern {item} [/ {item} [...]] / end
    actions {action} [/ {action} [...]] / end

Either check the validity of a flow rule or create it. Any number of pattern items and actions can be provided in any order. Completion is available for convenience.

This commit only adds support for the most basic item and action types, namely:

- END: terminates pattern items and actions lists.
- VOID: item/action filler, no operation.
- INVERT: inverted pattern matching, process packets that do not match.
- PASSTHRU: action that leaves packets up for additional processing by subsequent flow rules.

---
 app/test-pmd/cmdline.c      |  14 ++
 app/test-pmd/cmdline_flow.c | 314 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 327 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +14 -0
index 20a64b6..851cc16 100644
@@ -811,6 +811,20 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow validate {port_id}"
+			" [group {group_id}] [priority {level}]"
+			" [ingress] [egress]"
+			" pattern {item} [/ {item} [...]] / end"
+			" actions {action} [/ {action} [...]] / end\n"
+			"    Check whether a flow rule can be created.\n\n"
+
+			"flow create {port_id}"
+			" [group {group_id}] [priority {level}]"
+			" [ingress] [egress]"
+			" pattern {item} [{item} [...]] end"
+			" actions {action} [{action} [...]] end\n"
+			"    Create a flow rule.\n\n"
+
 			"flow destroy {port_id} rule {rule_id} [...]\n"
 			"    Destroy specific flow rules.\n\n"
 
app/test-pmd/cmdline_flow.c +313 -1

445 lines not on this page · raw message

Signature · 1 line
2.1.4
#90 lore raw

PATCH 11/22 app/testpmd: add flow query command

In reply to Adrien Mazarguil

Syntax:

flow query {port_id} {rule_id} {action}

Query a specific action of an existing flow rule.

---
 app/test-pmd/cmdline.c      |   3 +
 app/test-pmd/cmdline_flow.c | 121 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 123 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +3 -0
index 851cc16..edd1ee3 100644
@@ -831,6 +831,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"flow flush {port_id}\n"
 			"    Destroy all flow rules.\n\n"
 
+			"flow query {port_id} {rule_id} {action}\n"
+			"    Query an existing flow rule.\n\n"
+
 			"flow list {port_id} [group {group_id}] [...]\n"
 			"    List existing flow rules sorted by priority,"
 			" filtered by group identifiers.\n\n"
app/test-pmd/cmdline_flow.c +120 -1

186 lines not on this page · raw message

Signature · 1 line
2.1.4
#91 lore raw

PATCH 12/22 app/testpmd: add rte_flow item spec handler

In reply to Adrien Mazarguil

Add parser code to fully set individual fields of pattern item specification structures, using the following operators:

- fix: sets field and applies full bit-mask for perfect matching.
- spec: sets field without modifying its bit-mask.
- last: sets upper value of the spec => last range.
- mask: sets bit-mask affecting both spec and last from arbitrary value.

---
 app/test-pmd/cmdline_flow.c | 110 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
app/test-pmd/cmdline_flow.c +110 -0
index e70e8e2..790b4b8 100644
@@ -89,6 +89,10 @@ enum index {
 
 	/* Validate/create pattern. */
 	PATTERN,
+	ITEM_PARAM_FIX,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
 	ITEM_NEXT,
 	ITEM_END,
 	ITEM_VOID,
@@ -121,6 +125,7 @@ struct context {
 	uint16_t port; /**< Current port ID (for completions). */
 	uint32_t objdata; /**< Object-specific data. */
 	void *object; /**< Address of current object for relative offsets. */
+	void *objmask; /**< Object a full mask must be written to. */
 };
 
 /** Token argument. */
@@ -267,6 +272,14 @@ static const enum index next_list_attr[] = {
 	0,
 };
 
+static const enum index item_param[] = {
+	ITEM_PARAM_FIX,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
+	0,
+};
+
 static const enum index next_item[] = {
 	ITEM_END,
 	ITEM_VOID,
@@ -287,6 +300,8 @@ static int parse_init(struct context *, const struct token *,
 static int parse_vc(struct context *, const struct token *,
 		    const char *, unsigned int,
 		    void *, unsigned int);
+static int parse_vc_spec(struct context *, const struct token *,
+			 const char *, unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -492,6 +507,26 @@ static const struct token token_list[] = {
 		.next = NEXT(next_item),
 		.call = parse_vc,
 	},
+	[ITEM_PARAM_FIX] = {
+		.name = "fix",
+		.help = "match value perfectly (with full bit-mask)",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_SPEC] = {
+		.name = "spec",
+		.help = "match value according to configured bit-mask",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_LAST] = {
+		.name = "last",
+		.help = "specify upper bound to establish a range",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_MASK] = {
+		.name = "mask",
+		.help = "specify bit-mask with relevant bits set to one",
+		.call = parse_vc_spec,
+	},
 	[ITEM_NEXT] = {
 		.name = "/",
 		.help = "specify next pattern item",
@@ -605,6 +640,7 @@ parse_init(struct context *ctx, const struct token *token,
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
 	ctx->objdata = 0;
 	ctx->object = out;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -632,11 +668,13 @@ parse_vc(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.vc.data = (uint8_t *)out + size;
 		return len;
 	}
 	ctx->objdata = 0;
 	ctx->object = &out->args.vc.attr;
+	ctx->objmask = NULL;
 	switch (ctx->curr) {
 	case GROUP:
 	case PRIORITY:
@@ -652,6 +690,7 @@ parse_vc(struct context *ctx, const struct token *token,
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
 		ctx->object = out->args.vc.pattern;
+		ctx->objmask = NULL;
 		return len;
 	case ACTIONS:
 		out->args.vc.actions =
@@ -660,6 +699,7 @@ parse_vc(struct context *ctx, const struct token *token,
 						out->args.vc.pattern_n),
 					       sizeof(double));
 		ctx->object = out->args.vc.actions;
+		ctx->objmask = NULL;
 		return len;
 	default:
 		if (!token->priv)
@@ -682,6 +722,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.pattern_n;
 		ctx->object = item;
+		ctx->objmask = NULL;
 	} else {
 		const struct parse_action_priv *priv = token->priv;
 		struct rte_flow_action *action =
@@ -698,6 +739,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.actions_n;
 		ctx->object = action;
+		ctx->objmask = NULL;
 	}
 	memset(data, 0, data_size);
 	out->args.vc.data = data;
@@ -705,6 +747,60 @@ parse_vc(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse pattern item parameter type. */
+static int
+parse_vc_spec(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_item *item;
+	uint32_t data_size;
+	int index;
+	int objmask = 0;
+
+	(void)size;
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Parse parameter types. */
+	switch (ctx->curr) {
+	case ITEM_PARAM_FIX:
+		index = 0;
+		objmask = 1;
+		break;
+	case ITEM_PARAM_SPEC:
+		index = 0;
+		break;
+	case ITEM_PARAM_LAST:
+		index = 1;
+		break;
+	case ITEM_PARAM_MASK:
+		index = 2;
+		break;
+	default:
+		return -1;
+	}
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->args.vc.pattern_n)
+		return -1;
+	item = &out->args.vc.pattern[out->args.vc.pattern_n - 1];
+	data_size = ctx->objdata / 3; /* spec, last, mask */
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data + (data_size * index);
+	if (objmask) {
+		ctx->objmask = out->args.vc.data + (data_size * 2); /* mask */
+		item->mask = ctx->objmask;
+	} else
+		ctx->objmask = NULL;
+	/* Update relevant item pointer. */
+	*((const void **[]){ &item->spec, &item->last, &item->mask })[index] =
+		ctx->object;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -727,6 +823,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.destroy.rule =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -737,6 +834,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		return -1;
 	ctx->objdata = 0;
 	ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -762,6 +860,7 @@ parse_flush(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 	}
 	return len;
 }
@@ -788,6 +887,7 @@ parse_query(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 	}
 	return len;
 }
@@ -849,6 +949,7 @@ parse_list(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.list.group =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -859,6 +960,7 @@ parse_list(struct context *ctx, const struct token *token,
 		return -1;
 	ctx->objdata = 0;
 	ctx->object = out->args.list.group + out->args.list.group_n++;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -891,6 +993,7 @@ parse_int(struct context *ctx, const struct token *token,
 		return len;
 	buf = (uint8_t *)ctx->object + arg->offset;
 	size = arg->size;
+objmask:
 	switch (size) {
 	case sizeof(uint8_t):
 		*(uint8_t *)buf = u;
@@ -907,6 +1010,11 @@ parse_int(struct context *ctx, const struct token *token,
 	default:
 		goto error;
 	}
+	if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) {
+		u = -1;
+		buf = (uint8_t *)ctx->objmask + arg->offset;
+		goto objmask;
+	}
 	return len;
 error:
 	push_args(ctx, arg);
@@ -927,6 +1035,7 @@ parse_port(struct context *ctx, const struct token *token,
 	else {
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		size = sizeof(*out);
 	}
 	ret = parse_int(ctx, token, str, len, out, size);
@@ -1033,6 +1142,7 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->port = 0;
 	ctx->objdata = 0;
 	ctx->object = NULL;
+	ctx->objmask = NULL;
 }
 
 /** Parse a token (cmdline API). */
Signature · 1 line
2.1.4
#92 lore raw

Re: [PATCH 12/22] app/testpmd: add rte_flow item spec handler

Hi Adrien,

I try to setup the following rule, but it seems that after set 'spec' param, can not set 'mask' param, is it an issue here or am I wrong to use it ?

testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00
dst [TOKEN]: destination MAC
src [TOKEN]: source MAC
type [TOKEN]: EtherType
/ [TOKEN]: specify next pattern item

Best Regards
Yulong Pei

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Thursday, November 17, 2016 12:24 AM
To: [email protected]
Cc: Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>
Subject: [dpdk-dev] [PATCH 12/22] app/testpmd: add rte_flow item spec handler

Add parser code to fully set individual fields of pattern item specification structures, using the following operators:

- fix: sets field and applies full bit-mask for perfect matching.
- spec: sets field without modifying its bit-mask.
- last: sets upper value of the spec => last range.
- mask: sets bit-mask affecting both spec and last from arbitrary value.

---
 app/test-pmd/cmdline_flow.c | 110 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
app/test-pmd/cmdline_flow.c +95 -0
@@ -89,6 +89,10 @@ enum index {
 
 	/* Validate/create pattern. */
 	PATTERN,
+	ITEM_PARAM_FIX,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
 	ITEM_NEXT,
 	ITEM_END,
 	ITEM_VOID,
@@ -121,6 +125,7 @@ struct context {
 	uint16_t port; /**< Current port ID (for completions). */
 	uint32_t objdata; /**< Object-specific data. */
 	void *object; /**< Address of current object for relative offsets. */
+	void *objmask; /**< Object a full mask must be written to. */
 };
 
 /** Token argument. */
@@ -267,6 +272,14 @@ static const enum index next_list_attr[] = {
 	0,
 };
 
+static const enum index item_param[] = {
+	ITEM_PARAM_FIX,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
+	0,
+};
+
 static const enum index next_item[] = {
 	ITEM_END,
 	ITEM_VOID,
@@ -287,6 +300,8 @@ static int parse_init(struct context *, const struct token *,  static int parse_vc(struct context *, const struct token *,
 		    const char *, unsigned int,
 		    void *, unsigned int);
+static int parse_vc_spec(struct context *, const struct token *,
+			 const char *, unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -492,6 +507,26 @@ static const struct token token_list[] = {
 		.next = NEXT(next_item),
 		.call = parse_vc,
 	},
+	[ITEM_PARAM_FIX] = {
+		.name = "fix",
+		.help = "match value perfectly (with full bit-mask)",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_SPEC] = {
+		.name = "spec",
+		.help = "match value according to configured bit-mask",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_LAST] = {
+		.name = "last",
+		.help = "specify upper bound to establish a range",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_MASK] = {
+		.name = "mask",
+		.help = "specify bit-mask with relevant bits set to one",
+		.call = parse_vc_spec,
+	},
 	[ITEM_NEXT] = {
 		.name = "/",
 		.help = "specify next pattern item",
@@ -605,6 +640,7 @@ parse_init(struct context *ctx, const struct token *token,
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
 	ctx->objdata = 0;
 	ctx->object = out;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -632,11 +668,13 @@ parse_vc(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.vc.data = (uint8_t *)out + size;
 		return len;
 	}
 	ctx->objdata = 0;
 	ctx->object = &out->args.vc.attr;
+	ctx->objmask = NULL;
 	switch (ctx->curr) {
 	case GROUP:
 	case PRIORITY:
@@ -652,6 +690,7 @@ parse_vc(struct context *ctx, const struct token *token,
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
 		ctx->object = out->args.vc.pattern;
+		ctx->objmask = NULL;
 		return len;
 	case ACTIONS:
 		out->args.vc.actions =
@@ -660,6 +699,7 @@ parse_vc(struct context *ctx, const struct token *token,
 						out->args.vc.pattern_n),
 					       sizeof(double));
 		ctx->object = out->args.vc.actions;
+		ctx->objmask = NULL;
 		return len;
 	default:
 		if (!token->priv)
@@ -682,6 +722,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.pattern_n;
 		ctx->object = item;
+		ctx->objmask = NULL;
 	} else {
 		const struct parse_action_priv *priv = token->priv;
 		struct rte_flow_action *action =
@@ -698,6 +739,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.actions_n;
 		ctx->object = action;
+		ctx->objmask = NULL;
 	}
 	memset(data, 0, data_size);
 	out->args.vc.data = data;
@@ -705,6 +747,60 @@ parse_vc(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse pattern item parameter type. */ static int 
+parse_vc_spec(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_item *item;
+	uint32_t data_size;
+	int index;
+	int objmask = 0;
+
+	(void)size;
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Parse parameter types. */
+	switch (ctx->curr) {
+	case ITEM_PARAM_FIX:
+		index = 0;
+		objmask = 1;
+		break;
+	case ITEM_PARAM_SPEC:
+		index = 0;
+		break;
+	case ITEM_PARAM_LAST:
+		index = 1;
+		break;
+	case ITEM_PARAM_MASK:
+		index = 2;
+		break;
+	default:
+		return -1;
+	}
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->args.vc.pattern_n)
+		return -1;
+	item = &out->args.vc.pattern[out->args.vc.pattern_n - 1];
+	data_size = ctx->objdata / 3; /* spec, last, mask */
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data + (data_size * index);
+	if (objmask) {
+		ctx->objmask = out->args.vc.data + (data_size * 2); /* mask */
+		item->mask = ctx->objmask;
+	} else
+		ctx->objmask = NULL;
+	/* Update relevant item pointer. */
+	*((const void **[]){ &item->spec, &item->last, &item->mask })[index] =
+		ctx->object;
+	return len;
+}
+
 /** Parse tokens for destroy command. */  static int  parse_destroy(struct context *ctx, const struct token *token, @@ -727,6 +823,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;

ctx->object = out;
+ ctx->objmask = NULL;
out->args.destroy.rule =
(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
sizeof(double));
@@ -737,6 +834,7 @@ parse_destroy(struct context *ctx, const struct token *token,
return -1;
ctx->objdata = 0;
ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
+ ctx->objmask = NULL;
return len;
}

@@ -762,6 +860,7 @@ parse_flush(struct context *ctx, const struct token *token,
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
}
return len;
}
@@ -788,6 +887,7 @@ parse_query(struct context *ctx, const struct token *token,
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
}
return len;
}
@@ -849,6 +949,7 @@ parse_list(struct context *ctx, const struct token *token,
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
out->args.list.group =
(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
sizeof(double));
@@ -859,6 +960,7 @@ parse_list(struct context *ctx, const struct token *token,
return -1;
ctx->objdata = 0;
ctx->object = out->args.list.group + out->args.list.group_n++;
+ ctx->objmask = NULL;
return len;
}

@@ -891,6 +993,7 @@ parse_int(struct context *ctx, const struct token *token,
return len;
buf = (uint8_t *)ctx->object + arg->offset;
size = arg->size;
+objmask:
switch (size) {
case sizeof(uint8_t):
*(uint8_t *)buf = u;
@@ -907,6 +1010,11 @@ parse_int(struct context *ctx, const struct token *token,
default:
goto error;
}
+ if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) {
+ u = -1;
+ buf = (uint8_t *)ctx->objmask + arg->offset;
+ goto objmask;
+ }
return len;
error:
push_args(ctx, arg);
@@ -927,6 +1035,7 @@ parse_port(struct context *ctx, const struct token *token,
else {
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
size = sizeof(*out);
}
ret = parse_int(ctx, token, str, len, out, size); @@ -1033,6 +1142,7 @@ cmd_flow_context_init(struct context *ctx) ctx->port = 0;
ctx->objdata = 0;
ctx->object = NULL;
+ ctx->objmask = NULL;
}

/** Parse a token (cmdline API). */
--
2.1.4

#93 lore raw

Re: [PATCH 12/22] app/testpmd: add rte_flow item spec handler

Hi Yulong,

Quoted 9 lines · Yulong

Hi Adrien,

I try to setup the following rule, but it seems that after set 'spec' param, can not set 'mask' param, is it an issue here or am I wrong to use it ?

testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00
dst [TOKEN]: destination MAC
src [TOKEN]: source MAC
type [TOKEN]: EtherType
/ [TOKEN]: specify next pattern item

You need to re-specify dst with "mask" instead of "spec". You can specify it as many times you like to update each structure in turn, e.g.:

testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00 dst mask 00:00:00:00:ff:ff

If you want to specify both spec and mask at once assuming you want it full, these commands yield the same result:

testpmd> flow create 0 ingress pattern eth dst fix 00:00:00:00:09:00
testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00 dst mask ff:ff:ff:ff:ff:ff testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00 dst prefix 48

You are even allowed to change your mind:

testpmd> flow create 0 ingress pattern eth dst fix 00:00:2a:2a:2a:2a dst fix 00:00:00:00:09:00

All these will be properly documented in the v2 patchset. Note, this version will replace the "fix" keyword with "is" ("fix" made no sense according to feedback).

Signature · 2 lines
Adrien Mazarguil
6WIND
#94 lore raw

Re: [PATCH 12/22] app/testpmd: add rte_flow item spec handler

Thanks Adrien.

I have two questions:
1. when I set " / vlan tci fix 10" with testpmd, I find the mask of tci is 0xFFFF. Actually tci includes PRI, CFI, and Vlan_id which holds 12 bits, so is it possible to set the mask to 0xFFF?
Our driver will check the mask only covers vlan_id instead of the whole tci.

2. When we test destroy function, we find the pointer provided to PMD is NULL instead of the pointer PMD returned to RTE during creating flow. Could you please have double check? Thanks.

Best Regards
Beilei

Quoted 50 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Friday, December 16, 2016 5:18 PM
To: Pei, Yulong <[email protected]>
Cc: [email protected]; Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>; Xing, Beilei <[email protected]>
Subject: Re: [dpdk-dev] [PATCH 12/22] app/testpmd: add rte_flow item spec handler

Hi Yulong,

Quoted 3 lines · Yulong

Hi Adrien,

I try to setup the following rule, but it seems that after set 'spec' param,

can not set 'mask' param, is it an issue here or am I wrong to use it ?

Quoted 6 lines

testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00
dst [TOKEN]: destination MAC
src [TOKEN]: source MAC
type [TOKEN]: EtherType
/ [TOKEN]: specify next pattern item

You need to re-specify dst with "mask" instead of "spec". You can specify it as many times you like to update each structure in turn, e.g.:

testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00 dst
mask 00:00:00:00:ff:ff

If you want to specify both spec and mask at once assuming you want it full, these commands yield the same result:

testpmd> flow create 0 ingress pattern eth dst fix 00:00:00:00:09:00 testpmd>
flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00 dst mask ff:ff:ff:ff:ff:ff testpmd> flow create 0 ingress pattern eth dst spec 00:00:00:00:09:00 dst prefix 48

You are even allowed to change your mind:

testpmd> flow create 0 ingress pattern eth dst fix 00:00:2a:2a:2a:2a dst fix
00:00:00:00:09:00

All these will be properly documented in the v2 patchset. Note, this version will replace the "fix" keyword with "is" ("fix" made no sense according to feedback).

--
Adrien Mazarguil
6WIND

#95 lore raw

Re: [PATCH 12/22] app/testpmd: add rte_flow item spec handler

Hi Beilei,

Quoted 7 lines · Beilei

Thanks Adrien.

I have two questions:
1. when I set " / vlan tci fix 10" with testpmd, I find the mask of tci is 0xFFFF. Actually tci includes PRI, CFI, and Vlan_id which holds 12 bits, so is it possible to set the mask to 0xFFF?
Our driver will check the mask only covers vlan_id instead of the whole tci.

Right, I'll work on a method to do that. TCI remains 16 bit either way so the current approach remains accurate, although not convenient because a 10 bit mask must be specified manually.

This change won't be included in v2 though.

Quoted 3 lines

2. When we test destroy function, we find the pointer provided to PMD is NULL instead of the pointer PMD returned to RTE during creating flow. Could you please have double check? Thanks.

There is indeed a bug [1]. It is fixed in v2.

Thanks.

[1] http://dpdk.org/ml/archives/dev/2016-November/050435.html

Signature · 2 lines
Adrien Mazarguil
6WIND
#96 lore raw

PATCH 13/22 app/testpmd: add rte_flow item spec prefix length

In reply to Adrien Mazarguil

Generating bit-masks from prefix lengths is often more convenient than providing them entirely (e.g. to define IPv4 and IPv6 subnets).

This commit adds the "prefix" operator that assigns generated bit-masks to any pattern item specification field.

---
 app/test-pmd/cmdline_flow.c | 80 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)
app/test-pmd/cmdline_flow.c +80 -0
index 790b4b8..89307cb 100644
@@ -56,6 +56,7 @@ enum index {
 	/* Common tokens. */
 	INTEGER,
 	UNSIGNED,
+	PREFIX,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -93,6 +94,7 @@ enum index {
 	ITEM_PARAM_SPEC,
 	ITEM_PARAM_LAST,
 	ITEM_PARAM_MASK,
+	ITEM_PARAM_PREFIX,
 	ITEM_NEXT,
 	ITEM_END,
 	ITEM_VOID,
@@ -277,6 +279,7 @@ static const enum index item_param[] = {
 	ITEM_PARAM_SPEC,
 	ITEM_PARAM_LAST,
 	ITEM_PARAM_MASK,
+	ITEM_PARAM_PREFIX,
 	0,
 };
 
@@ -320,6 +323,9 @@ static int parse_list(struct context *, const struct token *,
 static int parse_int(struct context *, const struct token *,
 		     const char *, unsigned int,
 		     void *, unsigned int);
+static int parse_prefix(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -360,6 +366,13 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_none,
 	},
+	[PREFIX] = {
+		.name = "{prefix}",
+		.type = "PREFIX",
+		.help = "prefix length for bit-mask",
+		.call = parse_prefix,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -527,6 +540,11 @@ static const struct token token_list[] = {
 		.help = "specify bit-mask with relevant bits set to one",
 		.call = parse_vc_spec,
 	},
+	[ITEM_PARAM_PREFIX] = {
+		.name = "prefix",
+		.help = "generate bit-mask from a prefix length",
+		.call = parse_vc_spec,
+	},
 	[ITEM_NEXT] = {
 		.name = "/",
 		.help = "specify next pattern item",
@@ -604,6 +622,62 @@ push_args(struct context *ctx, const struct arg *arg)
 	return 0;
 }
 
+/**
+ * Parse a prefix length and generate a bit-mask.
+ *
+ * Last argument (ctx->args) is retrieved to determine mask size, storage
+ * location and whether the result must use network byte ordering.
+ */
+static int
+parse_prefix(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	static const uint8_t conv[] = "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe\xff";
+	char *end;
+	uintmax_t u;
+	unsigned int bytes;
+	unsigned int extra;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	errno = 0;
+	u = strtoumax(str, &end, 0);
+	if (errno || (size_t)(end - str) != len)
+		goto error;
+	bytes = u / 8;
+	extra = u % 8;
+	size = arg->size;
+	if (bytes > size || bytes + !!extra > size)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+	if (!arg->hton) {
+		memset((uint8_t *)buf + size - bytes, 0xff, bytes);
+		memset(buf, 0x00, size - bytes);
+		if (extra)
+			((uint8_t *)buf)[size - bytes - 1] = conv[extra];
+	} else
+#endif
+	{
+		memset(buf, 0xff, bytes);
+		memset((uint8_t *)buf + bytes, 0x00, size - bytes);
+		if (extra)
+			((uint8_t *)buf)[bytes] = conv[extra];
+	}
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Default parsing function for token name matching. */
 static int
 parse_default(struct context *ctx, const struct token *token,
@@ -775,6 +849,12 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 	case ITEM_PARAM_LAST:
 		index = 1;
 		break;
+	case ITEM_PARAM_PREFIX:
+		/* Modify next token to expect a prefix. */
+		if (ctx->next_num < 2)
+			return -1;
+		ctx->next[ctx->next_num - 2] = NEXT_ENTRY(PREFIX);
+		/* Fall through. */
 	case ITEM_PARAM_MASK:
 		index = 2;
 		break;
Signature · 1 line
2.1.4
#97 lore raw

PATCH 14/22 app/testpmd: add rte_flow bit-field support

In reply to Adrien Mazarguil

Several rte_flow structures expose bit-fields that cannot be set in a generic fashion at byte level. Add bit-mask support to handle them.

---
 app/test-pmd/cmdline_flow.c | 59 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)
app/test-pmd/cmdline_flow.c +59 -0
index 89307cb..81930e1 100644
@@ -136,6 +136,7 @@ struct arg {
 	uint32_t sign:1; /**< Value is signed. */
 	uint32_t offset; /**< Relative offset from ctx->object. */
 	uint32_t size; /**< Field size. */
+	const uint8_t *mask; /**< Bit-mask to use instead of offset/size. */
 };
 
 /** Parser token definition. */
@@ -195,6 +196,13 @@ struct token {
 		.size = sizeof(((s *)0)->f), \
 	})
 
+/** Static initializer for ARGS() to target a bit-field. */
+#define ARGS_ENTRY_BF(s, f) \
+	(&(const struct arg){ \
+		.size = sizeof(s), \
+		.mask = (const void *)&(const s){ .f = -1 }, \
+	})
+
 /** Static initializer for ARGS() to target a pointer. */
 #define ARGS_ENTRY_PTR(s, f) \
 	(&(const struct arg){ \
@@ -622,6 +630,34 @@ push_args(struct context *ctx, const struct arg *arg)
 	return 0;
 }
 
+/** Spread value into buffer according to bit-mask. */
+static size_t
+arg_entry_bf_fill(void *dst, uintmax_t val, const struct arg *arg)
+{
+	uint32_t i;
+	size_t len = 0;
+
+	/* Endian conversion is not supported on bit-fields. */
+	if (!arg->mask || arg->hton)
+		return 0;
+	for (i = 0; i != arg->size; ++i) {
+		unsigned int shift = 0;
+		uint8_t *buf = (uint8_t *)dst + i;
+
+		for (shift = 0; arg->mask[i] >> shift; ++shift) {
+			if (!(arg->mask[i] & (1 << shift)))
+				continue;
+			++len;
+			if (!dst)
+				continue;
+			*buf &= ~(1 << shift);
+			*buf |= (val & 1) << shift;
+			val >>= 1;
+		}
+	}
+	return len;
+}
+
 /**
  * Parse a prefix length and generate a bit-mask.
  *
@@ -648,6 +684,23 @@ parse_prefix(struct context *ctx, const struct token *token,
 	u = strtoumax(str, &end, 0);
 	if (errno || (size_t)(end - str) != len)
 		goto error;
+	if (arg->mask) {
+		uintmax_t v = 0;
+
+		extra = arg_entry_bf_fill(NULL, 0, arg);
+		if (u > extra)
+			goto error;
+		if (!ctx->object)
+			return len;
+		extra -= u;
+		while (u--)
+			(v <<= 1, v |= 1);
+		v <<= extra;
+		if (!arg_entry_bf_fill(ctx->object, v, arg) ||
+		    !arg_entry_bf_fill(ctx->objmask, -1, arg))
+			goto error;
+		return len;
+	}
 	bytes = u / 8;
 	extra = u % 8;
 	size = arg->size;
@@ -1071,6 +1124,12 @@ parse_int(struct context *ctx, const struct token *token,
 		goto error;
 	if (!ctx->object)
 		return len;
+	if (arg->mask) {
+		if (!arg_entry_bf_fill(ctx->object, u, arg) ||
+		    !arg_entry_bf_fill(ctx->objmask, -1, arg))
+			goto error;
+		return len;
+	}
 	buf = (uint8_t *)ctx->object + arg->offset;
 	size = arg->size;
 objmask:
Signature · 1 line
2.1.4
#98 lore raw

PATCH 15/22 app/testpmd: add item any to flow command

In reply to Adrien Mazarguil

This pattern item matches any protocol in place of the current layer and has two properties:

- min: minimum number of layers covered (0 or more).
- max: maximum number of layers covered (0 means infinity).

---
 app/test-pmd/cmdline_flow.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
app/test-pmd/cmdline_flow.c +30 -0
index 81930e1..5816be4 100644
@@ -99,6 +99,9 @@ enum index {
 	ITEM_END,
 	ITEM_VOID,
 	ITEM_INVERT,
+	ITEM_ANY,
+	ITEM_ANY_MIN,
+	ITEM_ANY_MAX,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -295,6 +298,14 @@ static const enum index next_item[] = {
 	ITEM_END,
 	ITEM_VOID,
 	ITEM_INVERT,
+	ITEM_ANY,
+	0,
+};
+
+static const enum index item_any[] = {
+	ITEM_ANY_MIN,
+	ITEM_ANY_MAX,
+	ITEM_NEXT,
 	0,
 };
 
@@ -579,6 +590,25 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
 		.call = parse_vc,
 	},
+	[ITEM_ANY] = {
+		.name = "any",
+		.help = "match any protocol for the current layer",
+		.priv = PRIV_ITEM(ANY, sizeof(struct rte_flow_item_any)),
+		.next = NEXT(item_any),
+		.call = parse_vc,
+	},
+	[ITEM_ANY_MIN] = {
+		.name = "min",
+		.help = "minimum number of layers covered",
+		.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, min)),
+	},
+	[ITEM_ANY_MAX] = {
+		.name = "max",
+		.help = "maximum number of layers covered, 0 for infinity",
+		.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, max)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
Signature · 1 line
2.1.4
#99 lore raw

PATCH 16/22 app/testpmd: add various items to flow command

In reply to Adrien Mazarguil

- PF: match packets addressed to the physical function.
- VF: match packets addressed to a virtual function ID.
- PORT: device-specific physical port index to use.

---
 app/test-pmd/cmdline_flow.c | 53 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
app/test-pmd/cmdline_flow.c +53 -0
index 5816be4..c61e31e 100644
@@ -102,6 +102,11 @@ enum index {
 	ITEM_ANY,
 	ITEM_ANY_MIN,
 	ITEM_ANY_MAX,
+	ITEM_PF,
+	ITEM_VF,
+	ITEM_VF_ID,
+	ITEM_PORT,
+	ITEM_PORT_INDEX,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -299,6 +304,9 @@ static const enum index next_item[] = {
 	ITEM_VOID,
 	ITEM_INVERT,
 	ITEM_ANY,
+	ITEM_PF,
+	ITEM_VF,
+	ITEM_PORT,
 	0,
 };
 
@@ -309,6 +317,18 @@ static const enum index item_any[] = {
 	0,
 };
 
+static const enum index item_vf[] = {
+	ITEM_VF_ID,
+	ITEM_NEXT,
+	0,
+};
+
+static const enum index item_port[] = {
+	ITEM_PORT_INDEX,
+	ITEM_NEXT,
+	0,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -609,6 +629,39 @@ static const struct token token_list[] = {
 		.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, max)),
 	},
+	[ITEM_PF] = {
+		.name = "pf",
+		.help = "match packets addressed to the physical function",
+		.priv = PRIV_ITEM(PF, 0),
+		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
+		.call = parse_vc,
+	},
+	[ITEM_VF] = {
+		.name = "vf",
+		.help = "match packets addressed to a virtual function ID",
+		.priv = PRIV_ITEM(VF, sizeof(struct rte_flow_item_vf)),
+		.next = NEXT(item_vf),
+		.call = parse_vc,
+	},
+	[ITEM_VF_ID] = {
+		.name = "id",
+		.help = "destination VF ID",
+		.next = NEXT(item_vf, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_vf, id)),
+	},
+	[ITEM_PORT] = {
+		.name = "port",
+		.help = "device-specific physical port index to use",
+		.priv = PRIV_ITEM(PORT, sizeof(struct rte_flow_item_port)),
+		.next = NEXT(item_port),
+		.call = parse_vc,
+	},
+	[ITEM_PORT_INDEX] = {
+		.name = "index",
+		.help = "physical port index",
+		.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
Signature · 1 line
2.1.4
#100 lore raw

PATCH 17/22 app/testpmd: add item raw to flow command

In reply to Adrien Mazarguil

Matches arbitrary byte strings with properties:

- relative: look for pattern after the previous item.
- search: search pattern from offset (see also limit).
- offset: absolute or relative offset for pattern.
- limit: search area limit for start of pattern.
- length: pattern length.
- pattern: byte string to look for.

---
 app/test-pmd/cmdline_flow.c | 206 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 206 insertions(+)
app/test-pmd/cmdline_flow.c +206 -0
index c61e31e..6f2f26c 100644
@@ -57,6 +57,8 @@ enum index {
 	INTEGER,
 	UNSIGNED,
 	PREFIX,
+	BOOLEAN,
+	STRING,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -107,6 +109,12 @@ enum index {
 	ITEM_VF_ID,
 	ITEM_PORT,
 	ITEM_PORT_INDEX,
+	ITEM_RAW,
+	ITEM_RAW_RELATIVE,
+	ITEM_RAW_SEARCH,
+	ITEM_RAW_OFFSET,
+	ITEM_RAW_LIMIT,
+	ITEM_RAW_PATTERN,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -116,6 +124,13 @@ enum index {
 	ACTION_PASSTHRU,
 };
 
+/** Size of pattern[] field in struct rte_flow_item_raw. */
+#define ITEM_RAW_PATTERN_SIZE 36
+
+/** Storage size for struct rte_flow_item_raw including pattern. */
+#define ITEM_RAW_SIZE \
+	(offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -217,6 +232,13 @@ struct token {
 		.size = sizeof(*((s *)0)->f), \
 	})
 
+/** Static initializer for ARGS() with arbitrary size. */
+#define ARGS_ENTRY_USZ(s, f, sz) \
+	(&(const struct arg){ \
+		.offset = offsetof(s, f), \
+		.size = (sz), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -307,6 +329,7 @@ static const enum index next_item[] = {
 	ITEM_PF,
 	ITEM_VF,
 	ITEM_PORT,
+	ITEM_RAW,
 	0,
 };
 
@@ -329,6 +352,16 @@ static const enum index item_port[] = {
 	0,
 };
 
+static const enum index item_raw[] = {
+	ITEM_RAW_RELATIVE,
+	ITEM_RAW_SEARCH,
+	ITEM_RAW_OFFSET,
+	ITEM_RAW_LIMIT,
+	ITEM_RAW_PATTERN,
+	ITEM_NEXT,
+	0,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -365,11 +398,19 @@ static int parse_int(struct context *, const struct token *,
 static int parse_prefix(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
+static int parse_boolean(struct context *, const struct token *,
+			 const char *, unsigned int,
+			 void *, unsigned int);
+static int parse_string(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
+static int comp_boolean(struct context *, const struct token *,
+			unsigned int, char *, unsigned int);
 static int comp_action(struct context *, const struct token *,
 		       unsigned int, char *, unsigned int);
 static int comp_port(struct context *, const struct token *,
@@ -412,6 +453,20 @@ static const struct token token_list[] = {
 		.call = parse_prefix,
 		.comp = comp_none,
 	},
+	[BOOLEAN] = {
+		.name = "{boolean}",
+		.type = "BOOLEAN",
+		.help = "any boolean value",
+		.call = parse_boolean,
+		.comp = comp_boolean,
+	},
+	[STRING] = {
+		.name = "{string}",
+		.type = "STRING",
+		.help = "fixed string",
+		.call = parse_string,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -662,6 +717,50 @@ static const struct token token_list[] = {
 		.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)),
 	},
+	[ITEM_RAW] = {
+		.name = "raw",
+		.help = "match an arbitrary byte string",
+		.priv = PRIV_ITEM(RAW, ITEM_RAW_SIZE),
+		.next = NEXT(item_raw),
+		.call = parse_vc,
+	},
+	[ITEM_RAW_RELATIVE] = {
+		.name = "relative",
+		.help = "look for pattern after the previous item",
+		.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw, relative)),
+	},
+	[ITEM_RAW_SEARCH] = {
+		.name = "search",
+		.help = "search pattern from offset (see also limit)",
+		.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw, search)),
+	},
+	[ITEM_RAW_OFFSET] = {
+		.name = "offset",
+		.help = "absolute or relative offset for pattern",
+		.next = NEXT(item_raw, NEXT_ENTRY(INTEGER), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, offset)),
+	},
+	[ITEM_RAW_LIMIT] = {
+		.name = "limit",
+		.help = "search area limit for start of pattern",
+		.next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, limit)),
+	},
+	[ITEM_RAW_PATTERN] = {
+		.name = "pattern",
+		.help = "byte string to look for",
+		.next = NEXT(item_raw,
+			     NEXT_ENTRY(STRING),
+			     NEXT_ENTRY(ITEM_PARAM_FIX,
+					ITEM_PARAM_SPEC,
+					ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, length),
+			     ARGS_ENTRY_USZ(struct rte_flow_item_raw,
+					    pattern,
+					    ITEM_RAW_PATTERN_SIZE)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1243,6 +1342,96 @@ parse_int(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse a string.
+ *
+ * Two arguments (ctx->args) are retrieved from the stack to store data and
+ * its length (in that order).
+ */
+static int
+parse_string(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg_data = pop_args(ctx);
+	const struct arg *arg_len = pop_args(ctx);
+	char tmp[16]; /* Ought to be enough. */
+	int ret;
+
+	/* Arguments are expected. */
+	if (!arg_data)
+		return -1;
+	if (!arg_len) {
+		push_args(ctx, arg_data);
+		return -1;
+	}
+	size = arg_data->size;
+	/* Bit-mask fill is not supported. */
+	if (arg_data->mask || size < len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	/* Let parse_int() fill length information first. */
+	ret = snprintf(tmp, sizeof(tmp), "%u", len);
+	if (ret < 0)
+		goto error;
+	push_args(ctx, arg_len);
+	ret = parse_int(ctx, token, tmp, ret, NULL, 0);
+	if (ret < 0) {
+		pop_args(ctx);
+		goto error;
+	}
+	buf = (uint8_t *)ctx->object + arg_data->offset;
+	/* Output buffer is not necessarily NUL-terminated. */
+	memcpy(buf, str, len);
+	memset((uint8_t *)buf + len, 0x55, size - len);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len);
+	return len;
+error:
+	push_args(ctx, arg_len);
+	push_args(ctx, arg_data);
+	return -1;
+}
+
+/** Boolean values (even indices stand for false). */
+static const char *const boolean_name[] = {
+	"0", "1",
+	"false", "true",
+	"no", "yes",
+	"N", "Y",
+	NULL,
+};
+
+/**
+ * Parse a boolean value.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_boolean(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	unsigned int i;
+	int ret;
+
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	for (i = 0; boolean_name[i]; ++i)
+		if (!strncmp(str, boolean_name[i], len))
+			break;
+	/* Process token as integer. */
+	if (boolean_name[i])
+		str = i & 1 ? "1" : "0";
+	push_args(ctx, arg);
+	ret = parse_int(ctx, token, str, strlen(str), buf, size);
+	return ret > 0 ? (int)len : ret;
+}
+
 /** Parse port and update context. */
 static int
 parse_port(struct context *ctx, const struct token *token,
@@ -1281,6 +1470,23 @@ comp_none(struct context *ctx, const struct token *token,
 	return 0;
 }
 
+/** Complete boolean values. */
+static int
+comp_boolean(struct context *ctx, const struct token *token,
+	     unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i;
+
+	(void)ctx;
+	(void)token;
+	for (i = 0; boolean_name[i]; ++i)
+		if (buf && i == ent)
+			return snprintf(buf, size, "%s", boolean_name[i]);
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Complete action names. */
 static int
 comp_action(struct context *ctx, const struct token *token,
Signature · 1 line
2.1.4
#101 lore raw

PATCH 18/22 app/testpmd: add items eth/vlan to flow command

In reply to Adrien Mazarguil

These pattern items match basic Ethernet headers (source, destination and type) and related 802.1Q/ad VLAN headers.

---
 app/test-pmd/cmdline_flow.c | 126 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
app/test-pmd/cmdline_flow.c +126 -0
index 6f2f26c..f2bd405 100644
@@ -43,6 +43,7 @@
 #include <rte_ethdev.h>
 #include <rte_byteorder.h>
 #include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
 #include <rte_flow.h>
 
 #include "testpmd.h"
@@ -59,6 +60,7 @@ enum index {
 	PREFIX,
 	BOOLEAN,
 	STRING,
+	MAC_ADDR,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -115,6 +117,13 @@ enum index {
 	ITEM_RAW_OFFSET,
 	ITEM_RAW_LIMIT,
 	ITEM_RAW_PATTERN,
+	ITEM_ETH,
+	ITEM_ETH_DST,
+	ITEM_ETH_SRC,
+	ITEM_ETH_TYPE,
+	ITEM_VLAN,
+	ITEM_VLAN_TPID,
+	ITEM_VLAN_TCI,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -239,6 +248,14 @@ struct token {
 		.size = (sz), \
 	})
 
+/** Same as ARGS_ENTRY() using network byte ordering. */
+#define ARGS_ENTRY_HTON(s, f) \
+	(&(const struct arg){ \
+		.hton = 1, \
+		.offset = offsetof(s, f), \
+		.size = sizeof(((s *)0)->f), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -330,6 +347,8 @@ static const enum index next_item[] = {
 	ITEM_VF,
 	ITEM_PORT,
 	ITEM_RAW,
+	ITEM_ETH,
+	ITEM_VLAN,
 	0,
 };
 
@@ -362,6 +381,21 @@ static const enum index item_raw[] = {
 	0,
 };
 
+static const enum index item_eth[] = {
+	ITEM_ETH_DST,
+	ITEM_ETH_SRC,
+	ITEM_ETH_TYPE,
+	ITEM_NEXT,
+	0,
+};
+
+static const enum index item_vlan[] = {
+	ITEM_VLAN_TPID,
+	ITEM_VLAN_TCI,
+	ITEM_NEXT,
+	0,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -404,6 +438,9 @@ static int parse_boolean(struct context *, const struct token *,
 static int parse_string(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
+static int parse_mac_addr(struct context *, const struct token *,
+			  const char *, unsigned int,
+			  void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -467,6 +504,13 @@ static const struct token token_list[] = {
 		.call = parse_string,
 		.comp = comp_none,
 	},
+	[MAC_ADDR] = {
+		.name = "{MAC address}",
+		.type = "MAC-48",
+		.help = "standard MAC address notation",
+		.call = parse_mac_addr,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -761,6 +805,50 @@ static const struct token token_list[] = {
 					    pattern,
 					    ITEM_RAW_PATTERN_SIZE)),
 	},
+	[ITEM_ETH] = {
+		.name = "eth",
+		.help = "match Ethernet header",
+		.priv = PRIV_ITEM(ETH, sizeof(struct rte_flow_item_eth)),
+		.next = NEXT(item_eth),
+		.call = parse_vc,
+	},
+	[ITEM_ETH_DST] = {
+		.name = "dst",
+		.help = "destination MAC",
+		.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, dst)),
+	},
+	[ITEM_ETH_SRC] = {
+		.name = "src",
+		.help = "source MAC",
+		.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, src)),
+	},
+	[ITEM_ETH_TYPE] = {
+		.name = "type",
+		.help = "EtherType",
+		.next = NEXT(item_eth, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_eth, type)),
+	},
+	[ITEM_VLAN] = {
+		.name = "vlan",
+		.help = "match 802.1Q/ad VLAN tag",
+		.priv = PRIV_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)),
+		.next = NEXT(item_vlan),
+		.call = parse_vc,
+	},
+	[ITEM_VLAN_TPID] = {
+		.name = "tpid",
+		.help = "tag protocol identifier",
+		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tpid)),
+	},
+	[ITEM_VLAN_TCI] = {
+		.name = "tci",
+		.help = "tag control information",
+		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tci)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1394,6 +1482,44 @@ parse_string(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse a MAC address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_mac_addr(struct context *ctx, const struct token *token,
+	       const char *str, unsigned int len,
+	       void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	struct ether_addr tmp;
+	int ret;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	ret = cmdline_parse_etheraddr(NULL, str, &tmp, size);
+	if (ret < 0 || (unsigned int)ret != len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Boolean values (even indices stand for false). */
 static const char *const boolean_name[] = {
 	"0", "1",
Signature · 1 line
2.1.4
#102 lore raw

PATCH 19/22 app/testpmd: add items ipv4/ipv6 to flow command

In reply to Adrien Mazarguil

Add the ability to match basic fields from IPv4 and IPv6 headers (source and destination addresses only).

---
 app/test-pmd/cmdline_flow.c | 177 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 177 insertions(+)
app/test-pmd/cmdline_flow.c +177 -0
index f2bd405..75096df 100644
@@ -38,6 +38,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include <string.h>
+#include <arpa/inet.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -61,6 +62,8 @@ enum index {
 	BOOLEAN,
 	STRING,
 	MAC_ADDR,
+	IPV4_ADDR,
+	IPV6_ADDR,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -124,6 +127,12 @@ enum index {
 	ITEM_VLAN,
 	ITEM_VLAN_TPID,
 	ITEM_VLAN_TCI,
+	ITEM_IPV4,
+	ITEM_IPV4_SRC,
+	ITEM_IPV4_DST,
+	ITEM_IPV6,
+	ITEM_IPV6_SRC,
+	ITEM_IPV6_DST,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -349,6 +358,8 @@ static const enum index next_item[] = {
 	ITEM_RAW,
 	ITEM_ETH,
 	ITEM_VLAN,
+	ITEM_IPV4,
+	ITEM_IPV6,
 	0,
 };
 
@@ -396,6 +407,20 @@ static const enum index item_vlan[] = {
 	0,
 };
 
+static const enum index item_ipv4[] = {
+	ITEM_IPV4_SRC,
+	ITEM_IPV4_DST,
+	ITEM_NEXT,
+	0,
+};
+
+static const enum index item_ipv6[] = {
+	ITEM_IPV6_SRC,
+	ITEM_IPV6_DST,
+	ITEM_NEXT,
+	0,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -441,6 +466,12 @@ static int parse_string(struct context *, const struct token *,
 static int parse_mac_addr(struct context *, const struct token *,
 			  const char *, unsigned int,
 			  void *, unsigned int);
+static int parse_ipv4_addr(struct context *, const struct token *,
+			   const char *, unsigned int,
+			   void *, unsigned int);
+static int parse_ipv6_addr(struct context *, const struct token *,
+			   const char *, unsigned int,
+			   void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -511,6 +542,20 @@ static const struct token token_list[] = {
 		.call = parse_mac_addr,
 		.comp = comp_none,
 	},
+	[IPV4_ADDR] = {
+		.name = "{IPv4 address}",
+		.type = "IPV4 ADDRESS",
+		.help = "standard IPv4 address notation",
+		.call = parse_ipv4_addr,
+		.comp = comp_none,
+	},
+	[IPV6_ADDR] = {
+		.name = "{IPv6 address}",
+		.type = "IPV6 ADDRESS",
+		.help = "standard IPv6 address notation",
+		.call = parse_ipv6_addr,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -849,6 +894,48 @@ static const struct token token_list[] = {
 		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tci)),
 	},
+	[ITEM_IPV4] = {
+		.name = "ipv4",
+		.help = "match IPv4 header",
+		.priv = PRIV_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)),
+		.next = NEXT(item_ipv4),
+		.call = parse_vc,
+	},
+	[ITEM_IPV4_SRC] = {
+		.name = "src",
+		.help = "source address",
+		.next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4,
+					     hdr.src_addr)),
+	},
+	[ITEM_IPV4_DST] = {
+		.name = "dst",
+		.help = "destination address",
+		.next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4,
+					     hdr.dst_addr)),
+	},
+	[ITEM_IPV6] = {
+		.name = "ipv6",
+		.help = "match IPv6 header",
+		.priv = PRIV_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)),
+		.next = NEXT(item_ipv6),
+		.call = parse_vc,
+	},
+	[ITEM_IPV6_SRC] = {
+		.name = "src",
+		.help = "source address",
+		.next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
+					     hdr.src_addr)),
+	},
+	[ITEM_IPV6_DST] = {
+		.name = "dst",
+		.help = "destination address",
+		.next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
+					     hdr.dst_addr)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1520,6 +1607,96 @@ parse_mac_addr(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse an IPv4 address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_ipv4_addr(struct context *ctx, const struct token *token,
+		const char *str, unsigned int len,
+		void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	char str2[len + 1];
+	struct in_addr tmp;
+	int ret;
+
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	/* Only network endian is supported. */
+	if (!arg->hton)
+		goto error;
+	memcpy(str2, str, len);
+	str2[len] = '\0';
+	ret = inet_pton(AF_INET, str2, &tmp);
+	if (ret != 1) {
+		/* Attempt integer parsing. */
+		push_args(ctx, arg);
+		return parse_int(ctx, token, str, len, buf, size);
+	}
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
+/**
+ * Parse an IPv6 address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_ipv6_addr(struct context *ctx, const struct token *token,
+		const char *str, unsigned int len,
+		void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	char str2[len + 1];
+	struct in6_addr tmp;
+	int ret;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	/* Only network endian is supported. */
+	if (!arg->hton)
+		goto error;
+	memcpy(str2, str, len);
+	str2[len] = '\0';
+	ret = inet_pton(AF_INET6, str2, &tmp);
+	if (ret != 1)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Boolean values (even indices stand for false). */
 static const char *const boolean_name[] = {
 	"0", "1",
Signature · 1 line
2.1.4
#103 lore raw

PATCH 20/22 app/testpmd: add L4 items to flow command

In reply to Adrien Mazarguil

Add the ability to match a few properties of common L4[.5] protocol headers:

- ICMP: type and code.
- UDP: source and destination ports.
- TCP: source and destination ports.
- SCTP: source and destination ports.
- VXLAN: network identifier.

---
 app/test-pmd/cmdline_flow.c | 163 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+)
app/test-pmd/cmdline_flow.c +163 -0
index 75096df..892f300 100644
@@ -133,6 +133,20 @@ enum index {
 	ITEM_IPV6,
 	ITEM_IPV6_SRC,
 	ITEM_IPV6_DST,
+	ITEM_ICMP,
+	ITEM_ICMP_TYPE,
+	ITEM_ICMP_CODE,
+	ITEM_UDP,
+	ITEM_UDP_SRC,
+	ITEM_UDP_DST,
+	ITEM_TCP,
+	ITEM_TCP_SRC,
+	ITEM_TCP_DST,
+	ITEM_SCTP,
+	ITEM_SCTP_SRC,
+	ITEM_SCTP_DST,
+	ITEM_VXLAN,
+	ITEM_VXLAN_VNI,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -360,6 +374,11 @@ static const enum index next_item[] = {
 	ITEM_VLAN,
 	ITEM_IPV4,
 	ITEM_IPV6,
+	ITEM_ICMP,
+	ITEM_UDP,
+	ITEM_TCP,
+	ITEM_SCTP,
+	ITEM_VXLAN,
 	0,
 };
 
@@ -421,6 +440,40 @@ static const enum index item_ipv6[] = {
 	0,
 };
 
+static const enum index item_icmp[] = {
+	ITEM_ICMP_TYPE,
+	ITEM_ICMP_CODE,
+	ITEM_NEXT,
+	0,
+};
+
+static const enum index item_udp[] = {
+	ITEM_UDP_SRC,
+	ITEM_UDP_DST,
+	ITEM_NEXT,
+	0,
+};
+
+static const enum index item_tcp[] = {
+	ITEM_TCP_SRC,
+	ITEM_TCP_DST,
+	ITEM_NEXT,
+	0,
+};
+
+static const enum index item_sctp[] = {
+	ITEM_SCTP_SRC,
+	ITEM_SCTP_DST,
+	ITEM_NEXT,
+	0,
+};
+
+static const enum index item_vxlan[] = {
+	ITEM_VXLAN_VNI,
+	ITEM_NEXT,
+	0,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -936,6 +989,103 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
 					     hdr.dst_addr)),
 	},
+	[ITEM_ICMP] = {
+		.name = "icmp",
+		.help = "match ICMP header",
+		.priv = PRIV_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)),
+		.next = NEXT(item_icmp),
+		.call = parse_vc,
+	},
+	[ITEM_ICMP_TYPE] = {
+		.name = "type",
+		.help = "ICMP packet type",
+		.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp,
+					     hdr.icmp_type)),
+	},
+	[ITEM_ICMP_CODE] = {
+		.name = "code",
+		.help = "ICMP packet code",
+		.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp,
+					     hdr.icmp_code)),
+	},
+	[ITEM_UDP] = {
+		.name = "udp",
+		.help = "match UDP header",
+		.priv = PRIV_ITEM(UDP, sizeof(struct rte_flow_item_udp)),
+		.next = NEXT(item_udp),
+		.call = parse_vc,
+	},
+	[ITEM_UDP_SRC] = {
+		.name = "src",
+		.help = "UDP source port",
+		.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp,
+					     hdr.src_port)),
+	},
+	[ITEM_UDP_DST] = {
+		.name = "dst",
+		.help = "UDP destination port",
+		.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp,
+					     hdr.dst_port)),
+	},
+	[ITEM_TCP] = {
+		.name = "tcp",
+		.help = "match TCP header",
+		.priv = PRIV_ITEM(TCP, sizeof(struct rte_flow_item_tcp)),
+		.next = NEXT(item_tcp),
+		.call = parse_vc,
+	},
+	[ITEM_TCP_SRC] = {
+		.name = "src",
+		.help = "TCP source port",
+		.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp,
+					     hdr.src_port)),
+	},
+	[ITEM_TCP_DST] = {
+		.name = "dst",
+		.help = "TCP destination port",
+		.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp,
+					     hdr.dst_port)),
+	},
+	[ITEM_SCTP] = {
+		.name = "sctp",
+		.help = "match SCTP header",
+		.priv = PRIV_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)),
+		.next = NEXT(item_sctp),
+		.call = parse_vc,
+	},
+	[ITEM_SCTP_SRC] = {
+		.name = "src",
+		.help = "SCTP source port",
+		.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp,
+					     hdr.src_port)),
+	},
+	[ITEM_SCTP_DST] = {
+		.name = "dst",
+		.help = "SCTP destination port",
+		.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp,
+					     hdr.dst_port)),
+	},
+	[ITEM_VXLAN] = {
+		.name = "vxlan",
+		.help = "match VXLAN header",
+		.priv = PRIV_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)),
+		.next = NEXT(item_vxlan),
+		.call = parse_vc,
+	},
+	[ITEM_VXLAN_VNI] = {
+		.name = "vni",
+		.help = "VXLAN identifier",
+		.next = NEXT(item_vxlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vxlan, vni)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1497,6 +1647,19 @@ parse_int(struct context *ctx, const struct token *token,
 	case sizeof(uint16_t):
 		*(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
 		break;
+	case sizeof(uint8_t [3]):
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+		if (!arg->hton) {
+			((uint8_t *)buf)[0] = u;
+			((uint8_t *)buf)[1] = u >> 8;
+			((uint8_t *)buf)[2] = u >> 16;
+			break;
+		}
+#endif
+		((uint8_t *)buf)[0] = u >> 16;
+		((uint8_t *)buf)[1] = u >> 8;
+		((uint8_t *)buf)[2] = u;
+		break;
 	case sizeof(uint32_t):
 		*(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
 		break;
Signature · 1 line
2.1.4
#104 lore raw

PATCH 21/22 app/testpmd: add various actions to flow command

In reply to Adrien Mazarguil

- MARK: attach 32 bit value to packets.
- FLAG: flag packets.
- DROP: drop packets.
- COUNT: enable counters for a rule.
- PF: redirect packets to physical device function.
- VF: redirect packets to virtual device function.

---
 app/test-pmd/cmdline_flow.c | 121 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)
app/test-pmd/cmdline_flow.c +121 -0
index 892f300..e166045 100644
@@ -154,6 +154,15 @@ enum index {
 	ACTION_END,
 	ACTION_VOID,
 	ACTION_PASSTHRU,
+	ACTION_MARK,
+	ACTION_MARK_ID,
+	ACTION_FLAG,
+	ACTION_DROP,
+	ACTION_COUNT,
+	ACTION_PF,
+	ACTION_VF,
+	ACTION_VF_ORIGINAL,
+	ACTION_VF_ID,
 };
 
 /** Size of pattern[] field in struct rte_flow_item_raw. */
@@ -478,6 +487,25 @@ static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
 	ACTION_PASSTHRU,
+	ACTION_MARK,
+	ACTION_FLAG,
+	ACTION_DROP,
+	ACTION_COUNT,
+	ACTION_PF,
+	ACTION_VF,
+	0,
+};
+
+static const enum index action_mark[] = {
+	ACTION_MARK_ID,
+	ACTION_NEXT,
+	0,
+};
+
+static const enum index action_vf[] = {
+	ACTION_VF_ORIGINAL,
+	ACTION_VF_ID,
+	ACTION_NEXT,
 	0,
 };
 
@@ -489,6 +517,8 @@ static int parse_vc(struct context *, const struct token *,
 		    void *, unsigned int);
 static int parse_vc_spec(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
+static int parse_vc_conf(struct context *, const struct token *,
+			 const char *, unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -1118,6 +1148,70 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_MARK] = {
+		.name = "mark",
+		.help = "attach 32 bit value to packets",
+		.priv = PRIV_ACTION(MARK, sizeof(struct rte_flow_action_mark)),
+		.next = NEXT(action_mark),
+		.call = parse_vc,
+	},
+	[ACTION_MARK_ID] = {
+		.name = "id",
+		.help = "32 bit value to return with packets",
+		.next = NEXT(action_mark, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_mark, id)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_FLAG] = {
+		.name = "flag",
+		.help = "flag packets",
+		.priv = PRIV_ACTION(FLAG, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_DROP] = {
+		.name = "drop",
+		.help = "drop packets (note: passthru has priority)",
+		.priv = PRIV_ACTION(DROP, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_COUNT] = {
+		.name = "count",
+		.help = "enable counters for this rule",
+		.priv = PRIV_ACTION(COUNT, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_PF] = {
+		.name = "pf",
+		.help = "redirect packets to physical device function",
+		.priv = PRIV_ACTION(PF, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_VF] = {
+		.name = "vf",
+		.help = "redirect packets to virtual device function",
+		.priv = PRIV_ACTION(VF, sizeof(struct rte_flow_action_vf)),
+		.next = NEXT(action_vf),
+		.call = parse_vc,
+	},
+	[ACTION_VF_ORIGINAL] = {
+		.name = "original",
+		.help = "use original VF ID if possible",
+		.next = NEXT(action_vf, NEXT_ENTRY(BOOLEAN)),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_action_vf,
+					   original)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_VF_ID] = {
+		.name = "id",
+		.help = "VF ID to redirect packets to",
+		.next = NEXT(action_vf, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_vf, id)),
+		.call = parse_vc_conf,
+	},
 };
 
 /** Remove and return last entry from argument stack. */
@@ -1441,6 +1535,33 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse action configuration field. */
+static int
+parse_vc_conf(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_action *action;
+
+	(void)size;
+	/* 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->args.vc.actions_n)
+		return -1;
+	action = &out->args.vc.actions[out->args.vc.actions_n - 1];
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data;
+	ctx->objmask = NULL;
+	/* Update configuration pointer. */
+	action->conf = ctx->object;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
Signature · 1 line
2.1.4
#105 lore raw

PATCH 22/22 app/testpmd: add queue actions to flow command

In reply to Adrien Mazarguil

- QUEUE: assign packets to a given queue index.
- DUP: duplicate packets to a given queue index.
- RSS: spread packets among several queues.

---
 app/test-pmd/cmdline_flow.c | 152 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 152 insertions(+)
app/test-pmd/cmdline_flow.c +152 -0
index e166045..70e2b76 100644
@@ -157,8 +157,15 @@ enum index {
 	ACTION_MARK,
 	ACTION_MARK_ID,
 	ACTION_FLAG,
+	ACTION_QUEUE,
+	ACTION_QUEUE_INDEX,
 	ACTION_DROP,
 	ACTION_COUNT,
+	ACTION_DUP,
+	ACTION_DUP_INDEX,
+	ACTION_RSS,
+	ACTION_RSS_QUEUES,
+	ACTION_RSS_QUEUE,
 	ACTION_PF,
 	ACTION_VF,
 	ACTION_VF_ORIGINAL,
@@ -172,6 +179,14 @@ enum index {
 #define ITEM_RAW_SIZE \
 	(offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
 
+/** Number of queue[] entries in struct rte_flow_action_rss. */
+#define ACTION_RSS_NUM 32
+
+/** Storage size for struct rte_flow_action_rss including queues. */
+#define ACTION_RSS_SIZE \
+	(offsetof(struct rte_flow_action_rss, queue) + \
+	 sizeof(*((struct rte_flow_action_rss *)0)->queue) * ACTION_RSS_NUM)
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -489,8 +504,11 @@ static const enum index next_action[] = {
 	ACTION_PASSTHRU,
 	ACTION_MARK,
 	ACTION_FLAG,
+	ACTION_QUEUE,
 	ACTION_DROP,
 	ACTION_COUNT,
+	ACTION_DUP,
+	ACTION_RSS,
 	ACTION_PF,
 	ACTION_VF,
 	0,
@@ -502,6 +520,24 @@ static const enum index action_mark[] = {
 	0,
 };
 
+static const enum index action_queue[] = {
+	ACTION_QUEUE_INDEX,
+	ACTION_NEXT,
+	0,
+};
+
+static const enum index action_dup[] = {
+	ACTION_DUP_INDEX,
+	ACTION_NEXT,
+	0,
+};
+
+static const enum index action_rss[] = {
+	ACTION_RSS_QUEUES,
+	ACTION_NEXT,
+	0,
+};
+
 static const enum index action_vf[] = {
 	ACTION_VF_ORIGINAL,
 	ACTION_VF_ID,
@@ -519,6 +555,9 @@ static int parse_vc_spec(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
 static int parse_vc_conf(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
+static int parse_vc_action_rss_queue(struct context *, const struct token *,
+				     const char *, unsigned int, void *,
+				     unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -568,6 +607,8 @@ static int comp_port(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_rule_id(struct context *, const struct token *,
 			unsigned int, char *, unsigned int);
+static int comp_vc_action_rss_queue(struct context *, const struct token *,
+				    unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -1169,6 +1210,21 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_QUEUE] = {
+		.name = "queue",
+		.help = "assign packets to a given queue index",
+		.priv = PRIV_ACTION(QUEUE,
+				    sizeof(struct rte_flow_action_queue)),
+		.next = NEXT(action_queue),
+		.call = parse_vc,
+	},
+	[ACTION_QUEUE_INDEX] = {
+		.name = "index",
+		.help = "queue index to use",
+		.next = NEXT(action_queue, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_queue, index)),
+		.call = parse_vc_conf,
+	},
 	[ACTION_DROP] = {
 		.name = "drop",
 		.help = "drop packets (note: passthru has priority)",
@@ -1183,6 +1239,39 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_DUP] = {
+		.name = "dup",
+		.help = "duplicate packets to a given queue index",
+		.priv = PRIV_ACTION(DUP, sizeof(struct rte_flow_action_dup)),
+		.next = NEXT(action_dup),
+		.call = parse_vc,
+	},
+	[ACTION_DUP_INDEX] = {
+		.name = "index",
+		.help = "queue index to duplicate packets to",
+		.next = NEXT(action_dup, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_dup, index)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_RSS] = {
+		.name = "rss",
+		.help = "spread packets among several queues",
+		.priv = PRIV_ACTION(RSS, ACTION_RSS_SIZE),
+		.next = NEXT(action_rss),
+		.call = parse_vc,
+	},
+	[ACTION_RSS_QUEUES] = {
+		.name = "queues",
+		.help = "queue indices to use",
+		.next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_QUEUE)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_RSS_QUEUE] = {
+		.name = "{queue}",
+		.help = "queue index",
+		.call = parse_vc_action_rss_queue,
+		.comp = comp_vc_action_rss_queue,
+	},
 	[ACTION_PF] = {
 		.name = "pf",
 		.help = "redirect packets to physical device function",
@@ -1562,6 +1651,51 @@ parse_vc_conf(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/**
+ * Parse queue field for RSS action.
+ *
+ * Valid tokens are queue indices and the "end" token.
+ */
+static int
+parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
+			  const char *str, unsigned int len,
+			  void *buf, unsigned int size)
+{
+	static const enum index const next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
+	int ret;
+	int i;
+
+	(void)token;
+	(void)buf;
+	(void)size;
+	if (ctx->curr != ACTION_RSS_QUEUE)
+		return -1;
+	i = ctx->objdata >> 16;
+	if (!strncmp(str, "end", len)) {
+		ctx->objdata &= 0xffff;
+		return len;
+	}
+	if (i >= ACTION_RSS_NUM)
+		return -1;
+	if (push_args(ctx, ARGS_ENTRY(struct rte_flow_action_rss, queue[i])))
+		return -1;
+	ret = parse_int(ctx, token, str, len, NULL, 0);
+	if (ret < 0) {
+		pop_args(ctx);
+		return -1;
+	}
+	++i;
+	ctx->objdata = i << 16 | (ctx->objdata & 0xffff);
+	/* Repeat token. */
+	if (ctx->next_num == RTE_DIM(ctx->next))
+		return -1;
+	ctx->next[ctx->next_num++] = next;
+	if (!ctx->object)
+		return len;
+	((struct rte_flow_action_rss *)ctx->object)->queues = i;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -2136,6 +2270,24 @@ comp_rule_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+/** Complete queue field for RSS action. */
+static int
+comp_vc_action_rss_queue(struct context *ctx, const struct token *token,
+			 unsigned int ent, char *buf, unsigned int size)
+{
+	static const char *const str[] = { "", "end", NULL };
+	unsigned int i;
+
+	(void)ctx;
+	(void)token;
+	for (i = 0; str[i] != NULL; ++i)
+		if (buf && i == ent)
+			return snprintf(buf, size, "%s", str[i]);
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
Signature · 1 line
2.1.4
#106 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

In reply to Adrien Mazarguil

Hi,

I found some small issues:

- In rte_flow_error_set(), *cause argument should be const to avoid a compilation error when we implement it.

- In port_flow_create(), the flow is not stored in the pf structure which ends by providing a null pointer to port_flow_destroy() function.

Regards,

Signature · 2 lines
Nélio Laranjeiro
6WIND
#107 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

In reply to Adrien Mazarguil

Hi Adrien,

I think that you already did test for your patchset, do you have any automated test scripts can be shared for validation since there did not have testpmd flow command documentation yet?

Best Regards
Yulong Pei

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Thursday, November 17, 2016 12:23 AM
To: [email protected]
Cc: Thomas Monjalon <[email protected]>; De Lara Guarch, Pablo <[email protected]>; Olivier Matz <[email protected]>
Subject: [dpdk-dev] [PATCH 00/22] Generic flow API (rte_flow)

As previously discussed in RFC v1 [1], RFC v2 [2], with changes described in [3] (also pasted below), here is the first non-draft series for this new API.

Its capabilities are so generic that its name had to be vague, it may be called "Generic flow API", "Generic flow interface" (possibly shortened as "GFI") to refer to the name of the new filter type, or "rte_flow" from the prefix used for its public symbols. I personally favor the latter.

While it is currently meant to supersede existing filter types in order for all PMDs to expose a common filtering/classification interface, it may eventually evolve to cover the following ideas as well:

- Rx/Tx offloads configuration through automatic offloads for specific packets, e.g. performing checksum on TCP packets could be expressed with an egress rule with a TCP pattern and a kind of checksum action.

- RSS configuration (already defined actually). Could be global or per rule depending on hardware capabilities.

- Switching configuration for devices with many physical ports; rules doing both ingress and egress could even be used to completely bypass software if supported by hardware.

[1] http://dpdk.org/ml/archives/dev/2016-July/043365.html
[2] http://dpdk.org/ml/archives/dev/2016-August/045383.html
[3] http://dpdk.org/ml/archives/dev/2016-November/050044.html

Changes since RFC v2:

- New separate VLAN pattern item (previously part of the ETH definition), found to be much more convenient.

- Removed useless "any" field from VF pattern item, the same effect can be achieved by not providing a specification structure.

- Replaced bit-fields from the VXLAN pattern item to avoid endianness conversion issues on 24-bit fields.

- Updated struct rte_flow_item with a new "last" field to create inclusive ranges. They are defined as the interval between (spec & mask) and (last & mask). All three parameters are optional.

- Renamed ID action MARK.

- Renamed "queue" fields in actions QUEUE and DUP to "index".

- "rss_conf" field in RSS action is now const.

- VF action now uses a 32 bit ID like its pattern item counterpart.

- Removed redundant struct rte_flow_pattern, API functions now expect struct
rte_flow_item lists terminated by END items.

- Replaced struct rte_flow_actions for the same reason, with struct rte_flow_action lists terminated by END actions.

- Error types (enum rte_flow_error_type) have been updated and the cause pointer in struct rte_flow_error is now const.

- Function prototypes (rte_flow_create, rte_flow_validate) have also been updated for clarity.

Additions:

- Public wrapper functions rte_flow_{validate|create|destroy|flush|query} are now implemented in rte_flow.c, with their symbols exported and versioned. Related filter type RTE_ETH_FILTER_GENERIC has been added.

- A separate header (rte_flow_driver.h) has been added for driver-side functionality, in particular struct rte_flow_ops which contains PMD callbacks returned by RTE_ETH_FILTER_GENERIC query.

- testpmd now exposes most of this API through the new "flow" command.

What remains to be done:

- Using endian-aware integer types (rte_beX_t) where necessary for clarity.

- API documentation (based on RFC).

- testpmd flow command documentation (although context-aware command completion should already help quite a bit in this regard).

- A few pattern item / action properties cannot be configured yet (e.g. rss_conf parameter for RSS action) and a few completions (e.g. possible queue IDs) should be added.

Adrien Mazarguil (22):
ethdev: introduce generic flow API
cmdline: add support for dynamic tokens
cmdline: add alignment constraint app/testpmd: implement basic support for rte_flow app/testpmd: add flow command app/testpmd: add rte_flow integer support app/testpmd: add flow list command app/testpmd: add flow flush command app/testpmd: add flow destroy command app/testpmd: add flow validate/create commands app/testpmd: add flow query command app/testpmd: add rte_flow item spec handler app/testpmd: add rte_flow item spec prefix length app/testpmd: add rte_flow bit-field support app/testpmd: add item any to flow command app/testpmd: add various items to flow command app/testpmd: add item raw to flow command app/testpmd: add items eth/vlan to flow command app/testpmd: add items ipv4/ipv6 to flow command app/testpmd: add L4 items to flow command app/testpmd: add various actions to flow command app/testpmd: add queue actions to flow command

 MAINTAINERS                            |    4 +
 app/test-pmd/Makefile                  |    1 +
 app/test-pmd/cmdline.c                 |   32 +
 app/test-pmd/cmdline_flow.c            | 2581 +++++++++++++++++++++++++++
 app/test-pmd/config.c                  |  484 +++++
 app/test-pmd/csumonly.c                |    1 +
 app/test-pmd/flowgen.c                 |    1 +
 app/test-pmd/icmpecho.c                |    1 +
 app/test-pmd/ieee1588fwd.c             |    1 +
 app/test-pmd/iofwd.c                   |    1 +
 app/test-pmd/macfwd.c                  |    1 +
 app/test-pmd/macswap.c                 |    1 +
 app/test-pmd/parameters.c              |    1 +
 app/test-pmd/rxonly.c                  |    1 +
 app/test-pmd/testpmd.c                 |    6 +
 app/test-pmd/testpmd.h                 |   27 +
 app/test-pmd/txonly.c                  |    1 +
 lib/librte_cmdline/cmdline_parse.c     |   67 +-
 lib/librte_cmdline/cmdline_parse.h     |   21 +
 lib/librte_ether/Makefile              |    3 +
 lib/librte_ether/rte_eth_ctrl.h        |    1 +
 lib/librte_ether/rte_ether_version.map |   10 +
 lib/librte_ether/rte_flow.c            |  159 ++
 lib/librte_ether/rte_flow.h            |  947 ++++++++++
 lib/librte_ether/rte_flow_driver.h     |  177 ++
 25 files changed, 4521 insertions(+), 9 deletions(-)  create mode 100644 app/test-pmd/cmdline_flow.c  create mode 100644 lib/librte_ether/rte_flow.c  create mode 100644 lib/librte_ether/rte_flow.h  create mode 100644 lib/librte_ether/rte_flow_driver.h

--
2.1.4

#108 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

Hi Yulong,

Quoted 3 lines · Yulong

Hi Adrien,

I think that you already did test for your patchset, do you have any automated test scripts can be shared for validation since there did not have testpmd flow command documentation yet?

No automated script, at least not yet. I intend to submit v2 with extra API documentation, testpmd commands with examples of expected behavior and output, as well as fixes for the issues pointed out by Nelio.

Signature · 2 lines
Adrien Mazarguil
6WIND
#109 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

In reply to Adrien Mazarguil

Hi Adrien,

Quoted 80 lines · Adrien Mazarguil

As previously discussed in RFC v1 [1], RFC v2 [2], with changes described in [3] (also pasted below), here is the first non-draft series for this new API.

Its capabilities are so generic that its name had to be vague, it may be called "Generic flow API", "Generic flow interface" (possibly shortened as "GFI") to refer to the name of the new filter type, or "rte_flow" from the prefix used for its public symbols. I personally favor the latter.

While it is currently meant to supersede existing filter types in order for all PMDs to expose a common filtering/classification interface, it may eventually evolve to cover the following ideas as well:

- Rx/Tx offloads configuration through automatic offloads for specific packets, e.g. performing checksum on TCP packets could be expressed with an egress rule with a TCP pattern and a kind of checksum action.

- RSS configuration (already defined actually). Could be global or per rule depending on hardware capabilities.

- Switching configuration for devices with many physical ports; rules doing both ingress and egress could even be used to completely bypass software if supported by hardware.

[1] http://dpdk.org/ml/archives/dev/2016-July/043365.html
[2] http://dpdk.org/ml/archives/dev/2016-August/045383.html
[3] http://dpdk.org/ml/archives/dev/2016-November/050044.html

Changes since RFC v2:

- New separate VLAN pattern item (previously part of the ETH definition), found to be much more convenient.

- Removed useless "any" field from VF pattern item, the same effect can be achieved by not providing a specification structure.

- Replaced bit-fields from the VXLAN pattern item to avoid endianness conversion issues on 24-bit fields.

- Updated struct rte_flow_item with a new "last" field to create inclusive ranges. They are defined as the interval between (spec & mask) and (last & mask). All three parameters are optional.

- Renamed ID action MARK.

- Renamed "queue" fields in actions QUEUE and DUP to "index".

- "rss_conf" field in RSS action is now const.

- VF action now uses a 32 bit ID like its pattern item counterpart.

- Removed redundant struct rte_flow_pattern, API functions now expect struct
rte_flow_item lists terminated by END items.

- Replaced struct rte_flow_actions for the same reason, with struct rte_flow_action lists terminated by END actions.

- Error types (enum rte_flow_error_type) have been updated and the cause pointer in struct rte_flow_error is now const.

- Function prototypes (rte_flow_create, rte_flow_validate) have also been updated for clarity.

Additions:

- Public wrapper functions rte_flow_{validate|create|destroy|flush|query} are now implemented in rte_flow.c, with their symbols exported and versioned. Related filter type RTE_ETH_FILTER_GENERIC has been added.

- A separate header (rte_flow_driver.h) has been added for driver-side functionality, in particular struct rte_flow_ops which contains PMD callbacks returned by RTE_ETH_FILTER_GENERIC query.

- testpmd now exposes most of this API through the new "flow" command.

What remains to be done:

- Using endian-aware integer types (rte_beX_t) where necessary for clarity.

Quoted 9 lines

- API documentation (based on RFC).

- testpmd flow command documentation (although context-aware command completion should already help quite a bit in this regard).

- A few pattern item / action properties cannot be configured yet (e.g. rss_conf parameter for RSS action) and a few completions (e.g. possible queue IDs) should be added.

<...>

I was trying to check driver filter API patches, but hit a few compiler errors with this patchset.

[1] clang complains about variable bitfield value changed from -1 to 1. Which is correct, but I guess that is intentional, but I don't know how to tell this to clang?

[2] shred library compilation error, because of missing rte_flow_flush in rte_ether_version.map file

[3] bunch of icc compilation errors, almost all are same type:
error #188: enumerated type mixed with another type

Thanks,
ferruh

[1]
=============================
.../app/test-pmd/cmdline_flow.c:944:16: error: implicit truncation from 'int' to bitfield changes value from -1 to 1 [-Werror,-Wbitfield-constant-conversion]
.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
relative)),

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../app/test-pmd/cmdline_flow.c:282:42: note: expanded from macro 'ARGS_ENTRY_BF'

                .mask = (const void *)&(const s){ .f = -1 }, \
                                                       ^~
.../app/test-pmd/cmdline_flow.c:269:49: note: expanded from macro 'ARGS'
#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
                                                ^~~~~~~~~~~

.../app/test-pmd/cmdline_flow.c:950:16: error: implicit truncation from 'int' to bitfield changes value from -1 to 1 [-Werror,-Wbitfield-constant-conversion]
.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
search)),

                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../app/test-pmd/cmdline_flow.c:282:42: note: expanded from macro
'ARGS_ENTRY_BF'
                .mask = (const void *)&(const s){ .f = -1 }, \
                                                       ^~
.../app/test-pmd/cmdline_flow.c:269:49: note: expanded from macro 'ARGS'
#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
                                                ^~~~~~~~~~~

.../app/test-pmd/cmdline_flow.c:1293:16: error: implicit truncation from 'int' to bitfield changes value from -1 to 1 [-Werror,-Wbitfield-constant-conversion]
.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_action_vf,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../app/test-pmd/cmdline_flow.c:282:42: note: expanded from macro 'ARGS_ENTRY_BF'

                .mask = (const void *)&(const s){ .f = -1 }, \
                                                       ^~
.../app/test-pmd/cmdline_flow.c:269:49: note: expanded from macro 'ARGS'
#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
                                                ^~~~~~~~~~~
.../app/test-pmd/cmdline_flow.c:1664:26: error: duplicate 'const'
declaration specifier [-Werror,-Wduplicate-decl-specifier]
        static const enum index const next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
                                ^

4 errors generated.

[2]
=============================
LD testpmd
config.o: In function `port_flow_flush':
config.c:(.text+0x2231): undefined reference to `rte_flow_flush'
collect2: error: ld returned 1 exit status

[3]
=============================
.../app/test-pmd/cmdline_flow.c(364): error #188: enumerated type mixed with another type

        0,


        ^

.../app/test-pmd/cmdline_flow.c(370): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(376): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(385): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(406): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(413): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(419): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(425): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(435): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(443): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(450): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(457): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(464): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(471): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(478): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(485): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(492): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(498): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(514): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(520): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(526): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(532): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(538): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(545): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(619): error #188: enumerated type mixed with another type

                .next = NEXT(NEXT_ENTRY(FLOW)),
                        ^

.../app/test-pmd/cmdline_flow.c(716): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY
^

.../app/test-pmd/cmdline_flow.c(729): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(736): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(743): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(743): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(750): error #188: enumerated type mixed with another type

                .next = NEXT(NEXT_ENTRY(PORT_ID)),
                        ^

.../app/test-pmd/cmdline_flow.c(757): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(QUERY_ACTION),
^

.../app/test-pmd/cmdline_flow.c(757): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(QUERY_ACTION),
^

.../app/test-pmd/cmdline_flow.c(757): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(QUERY_ACTION),
^

.../app/test-pmd/cmdline_flow.c(768): error #188: enumerated type mixed with another type
.next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(776): error #188: enumerated type mixed with another type
.next = NEXT(next_destroy_attr, NEXT_ENTRY(RULE_ID)),
^

.../app/test-pmd/cmdline_flow.c(792): error #188: enumerated type mixed with another type
.next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)),
^

.../app/test-pmd/cmdline_flow.c(800): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(GROUP_ID)),
^

.../app/test-pmd/cmdline_flow.c(807): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(PRIORITY_LEVEL)),
^

.../app/test-pmd/cmdline_flow.c(864): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTIONS)),
^

.../app/test-pmd/cmdline_flow.c(871): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(878): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(891): error #188: enumerated type mixed with another type
.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(897): error #188: enumerated type mixed with another type
.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(904): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(917): error #188: enumerated type mixed with another type
.next = NEXT(item_vf, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(930): error #188: enumerated type mixed with another type
.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(943): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
^

.../app/test-pmd/cmdline_flow.c(949): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
^

.../app/test-pmd/cmdline_flow.c(955): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(INTEGER), item_param),
^

.../app/test-pmd/cmdline_flow.c(961): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(967): error #188: enumerated type mixed with another type
.next = NEXT(item_raw,
^

.../app/test-pmd/cmdline_flow.c(967): error #188: enumerated type mixed with another type
.next = NEXT(item_raw,
^

.../app/test-pmd/cmdline_flow.c(987): error #188: enumerated type mixed with another type
.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
^

.../app/test-pmd/cmdline_flow.c(993): error #188: enumerated type mixed with another type
.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
^

.../app/test-pmd/cmdline_flow.c(999): error #188: enumerated type mixed with another type
.next = NEXT(item_eth, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1012): error #188: enumerated type mixed with another type
.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1018): error #188: enumerated type mixed with another type
.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1031): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1038): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1052): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1059): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1073): error #188: enumerated type mixed with another type
.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1080): error #188: enumerated type mixed with another type
.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1094): error #188: enumerated type mixed with another type
.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1101): error #188: enumerated type mixed with another type
.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1115): error #188: enumerated type mixed with another type
.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1122): error #188: enumerated type mixed with another type
.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1136): error #188: enumerated type mixed with another type
.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1143): error #188: enumerated type mixed with another type
.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1157): error #188: enumerated type mixed with another type
.next = NEXT(item_vxlan, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1182): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1189): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1202): error #188: enumerated type mixed with another type
.next = NEXT(action_mark, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1210): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1224): error #188: enumerated type mixed with another type
.next = NEXT(action_queue, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1232): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1239): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1252): error #188: enumerated type mixed with another type
.next = NEXT(action_dup, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1266): error #188: enumerated type mixed with another type
.next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_QUEUE)),
^

.../app/test-pmd/cmdline_flow.c(1279): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1292): error #188: enumerated type mixed with another type
.next = NEXT(action_vf, NEXT_ENTRY(BOOLEAN)),
^

.../app/test-pmd/cmdline_flow.c(1300): error #188: enumerated type mixed with another type
.next = NEXT(action_vf, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1599): error #188: enumerated type mixed with another type

                ctx->next[ctx->next_num - 2] = NEXT_ENTRY(PREFIX);
                                               ^

.../app/test-pmd/cmdline_flow.c(1664): error #83: type qualifier specified more than once

        static const enum index const next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
                                ^

.../app/test-pmd/cmdline_flow.c(1664): error #188: enumerated type mixed with another type

        static const enum index const next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
                                               ^

.../app/test-pmd/cmdline_flow.c(2302): error #188: enumerated type mixed with another type

        ctx->curr = 0;
                  ^

.../app/test-pmd/cmdline_flow.c(2303): error #188: enumerated type mixed with another type

        ctx->prev = 0;
                  ^
#110 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

Hi Ferruh,

Quoted 102 lines · Ferruh Yigit

Hi Adrien,

Quoted 80 lines · Adrien Mazarguil

As previously discussed in RFC v1 [1], RFC v2 [2], with changes described in [3] (also pasted below), here is the first non-draft series for this new API.

Its capabilities are so generic that its name had to be vague, it may be called "Generic flow API", "Generic flow interface" (possibly shortened as "GFI") to refer to the name of the new filter type, or "rte_flow" from the prefix used for its public symbols. I personally favor the latter.

While it is currently meant to supersede existing filter types in order for all PMDs to expose a common filtering/classification interface, it may eventually evolve to cover the following ideas as well:

- Rx/Tx offloads configuration through automatic offloads for specific packets, e.g. performing checksum on TCP packets could be expressed with an egress rule with a TCP pattern and a kind of checksum action.

- RSS configuration (already defined actually). Could be global or per rule depending on hardware capabilities.

- Switching configuration for devices with many physical ports; rules doing both ingress and egress could even be used to completely bypass software if supported by hardware.

[1] http://dpdk.org/ml/archives/dev/2016-July/043365.html
[2] http://dpdk.org/ml/archives/dev/2016-August/045383.html
[3] http://dpdk.org/ml/archives/dev/2016-November/050044.html

Changes since RFC v2:

- New separate VLAN pattern item (previously part of the ETH definition), found to be much more convenient.

- Removed useless "any" field from VF pattern item, the same effect can be achieved by not providing a specification structure.

- Replaced bit-fields from the VXLAN pattern item to avoid endianness conversion issues on 24-bit fields.

- Updated struct rte_flow_item with a new "last" field to create inclusive ranges. They are defined as the interval between (spec & mask) and (last & mask). All three parameters are optional.

- Renamed ID action MARK.

- Renamed "queue" fields in actions QUEUE and DUP to "index".

- "rss_conf" field in RSS action is now const.

- VF action now uses a 32 bit ID like its pattern item counterpart.

- Removed redundant struct rte_flow_pattern, API functions now expect struct
rte_flow_item lists terminated by END items.

- Replaced struct rte_flow_actions for the same reason, with struct rte_flow_action lists terminated by END actions.

- Error types (enum rte_flow_error_type) have been updated and the cause pointer in struct rte_flow_error is now const.

- Function prototypes (rte_flow_create, rte_flow_validate) have also been updated for clarity.

Additions:

- Public wrapper functions rte_flow_{validate|create|destroy|flush|query} are now implemented in rte_flow.c, with their symbols exported and versioned. Related filter type RTE_ETH_FILTER_GENERIC has been added.

- A separate header (rte_flow_driver.h) has been added for driver-side functionality, in particular struct rte_flow_ops which contains PMD callbacks returned by RTE_ETH_FILTER_GENERIC query.

- testpmd now exposes most of this API through the new "flow" command.

What remains to be done:

- Using endian-aware integer types (rte_beX_t) where necessary for clarity.

Quoted 9 lines

- API documentation (based on RFC).

- testpmd flow command documentation (although context-aware command completion should already help quite a bit in this regard).

- A few pattern item / action properties cannot be configured yet (e.g. rss_conf parameter for RSS action) and a few completions (e.g. possible queue IDs) should be added.

<...>

I was trying to check driver filter API patches, but hit a few compiler errors with this patchset.

[1] clang complains about variable bitfield value changed from -1 to 1. Which is correct, but I guess that is intentional, but I don't know how to tell this to clang?

Quoted 5 lines

[2] shred library compilation error, because of missing rte_flow_flush in rte_ether_version.map file

[3] bunch of icc compilation errors, almost all are same type:
error #188: enumerated type mixed with another type

Thanks for the report, I'll attempt to address them all in v2. However icc error #188 looks like a pain, I think I can work around it but do we really not tolerate the use of normal integers inside enum fields in DPDK?

Quoted 503 lines

[1]
=============================
.../app/test-pmd/cmdline_flow.c:944:16: error: implicit truncation from 'int' to bitfield changes value from -1 to 1 [-Werror,-Wbitfield-constant-conversion]
.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
relative)),

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .../app/test-pmd/cmdline_flow.c:282:42: note: expanded from macro 'ARGS_ENTRY_BF'

                .mask = (const void *)&(const s){ .f = -1 }, \
                                                       ^~
.../app/test-pmd/cmdline_flow.c:269:49: note: expanded from macro 'ARGS'
#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
                                                ^~~~~~~~~~~

.../app/test-pmd/cmdline_flow.c:950:16: error: implicit truncation from 'int' to bitfield changes value from -1 to 1 [-Werror,-Wbitfield-constant-conversion]
.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
search)),

                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../app/test-pmd/cmdline_flow.c:282:42: note: expanded from macro
'ARGS_ENTRY_BF'
                .mask = (const void *)&(const s){ .f = -1 }, \
                                                       ^~
.../app/test-pmd/cmdline_flow.c:269:49: note: expanded from macro 'ARGS'
#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
                                                ^~~~~~~~~~~

.../app/test-pmd/cmdline_flow.c:1293:16: error: implicit truncation from 'int' to bitfield changes value from -1 to 1 [-Werror,-Wbitfield-constant-conversion]
.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_action_vf,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../app/test-pmd/cmdline_flow.c:282:42: note: expanded from macro 'ARGS_ENTRY_BF'

                .mask = (const void *)&(const s){ .f = -1 }, \
                                                       ^~
.../app/test-pmd/cmdline_flow.c:269:49: note: expanded from macro 'ARGS'
#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
                                                ^~~~~~~~~~~
.../app/test-pmd/cmdline_flow.c:1664:26: error: duplicate 'const'
declaration specifier [-Werror,-Wduplicate-decl-specifier]
        static const enum index const next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
                                ^

4 errors generated.

[2]
=============================
LD testpmd
config.o: In function `port_flow_flush':
config.c:(.text+0x2231): undefined reference to `rte_flow_flush'
collect2: error: ld returned 1 exit status

[3]
=============================
.../app/test-pmd/cmdline_flow.c(364): error #188: enumerated type mixed with another type

        0,


        ^

.../app/test-pmd/cmdline_flow.c(370): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(376): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(385): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(406): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(413): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(419): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(425): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(435): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(443): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(450): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(457): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(464): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(471): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(478): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(485): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(492): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(498): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(514): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(520): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(526): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(532): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(538): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(545): error #188: enumerated type mixed with another type

        0,
        ^

.../app/test-pmd/cmdline_flow.c(619): error #188: enumerated type mixed with another type

                .next = NEXT(NEXT_ENTRY(FLOW)),
                        ^

.../app/test-pmd/cmdline_flow.c(716): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY
^

.../app/test-pmd/cmdline_flow.c(729): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(736): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(743): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(743): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(750): error #188: enumerated type mixed with another type

                .next = NEXT(NEXT_ENTRY(PORT_ID)),
                        ^

.../app/test-pmd/cmdline_flow.c(757): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(QUERY_ACTION),
^

.../app/test-pmd/cmdline_flow.c(757): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(QUERY_ACTION),
^

.../app/test-pmd/cmdline_flow.c(757): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(QUERY_ACTION),
^

.../app/test-pmd/cmdline_flow.c(768): error #188: enumerated type mixed with another type
.next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)),
^

.../app/test-pmd/cmdline_flow.c(776): error #188: enumerated type mixed with another type
.next = NEXT(next_destroy_attr, NEXT_ENTRY(RULE_ID)),
^

.../app/test-pmd/cmdline_flow.c(792): error #188: enumerated type mixed with another type
.next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)),
^

.../app/test-pmd/cmdline_flow.c(800): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(GROUP_ID)),
^

.../app/test-pmd/cmdline_flow.c(807): error #188: enumerated type mixed with another type
.next = NEXT(next_vc_attr, NEXT_ENTRY(PRIORITY_LEVEL)),
^

.../app/test-pmd/cmdline_flow.c(864): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTIONS)),
^

.../app/test-pmd/cmdline_flow.c(871): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(878): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(891): error #188: enumerated type mixed with another type
.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(897): error #188: enumerated type mixed with another type
.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(904): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(917): error #188: enumerated type mixed with another type
.next = NEXT(item_vf, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(930): error #188: enumerated type mixed with another type
.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(943): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
^

.../app/test-pmd/cmdline_flow.c(949): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
^

.../app/test-pmd/cmdline_flow.c(955): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(INTEGER), item_param),
^

.../app/test-pmd/cmdline_flow.c(961): error #188: enumerated type mixed with another type
.next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(967): error #188: enumerated type mixed with another type
.next = NEXT(item_raw,
^

.../app/test-pmd/cmdline_flow.c(967): error #188: enumerated type mixed with another type
.next = NEXT(item_raw,
^

.../app/test-pmd/cmdline_flow.c(987): error #188: enumerated type mixed with another type
.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
^

.../app/test-pmd/cmdline_flow.c(993): error #188: enumerated type mixed with another type
.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
^

.../app/test-pmd/cmdline_flow.c(999): error #188: enumerated type mixed with another type
.next = NEXT(item_eth, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1012): error #188: enumerated type mixed with another type
.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1018): error #188: enumerated type mixed with another type
.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1031): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1038): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1052): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1059): error #188: enumerated type mixed with another type

                .next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
                        ^

.../app/test-pmd/cmdline_flow.c(1073): error #188: enumerated type mixed with another type
.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1080): error #188: enumerated type mixed with another type
.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1094): error #188: enumerated type mixed with another type
.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1101): error #188: enumerated type mixed with another type
.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1115): error #188: enumerated type mixed with another type
.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1122): error #188: enumerated type mixed with another type
.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1136): error #188: enumerated type mixed with another type
.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1143): error #188: enumerated type mixed with another type
.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1157): error #188: enumerated type mixed with another type
.next = NEXT(item_vxlan, NEXT_ENTRY(UNSIGNED), item_param),
^

.../app/test-pmd/cmdline_flow.c(1182): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1189): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1202): error #188: enumerated type mixed with another type
.next = NEXT(action_mark, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1210): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1224): error #188: enumerated type mixed with another type
.next = NEXT(action_queue, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1232): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1239): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1252): error #188: enumerated type mixed with another type
.next = NEXT(action_dup, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1266): error #188: enumerated type mixed with another type
.next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_QUEUE)),
^

.../app/test-pmd/cmdline_flow.c(1279): error #188: enumerated type mixed with another type
.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
^

.../app/test-pmd/cmdline_flow.c(1292): error #188: enumerated type mixed with another type
.next = NEXT(action_vf, NEXT_ENTRY(BOOLEAN)),
^

.../app/test-pmd/cmdline_flow.c(1300): error #188: enumerated type mixed with another type
.next = NEXT(action_vf, NEXT_ENTRY(UNSIGNED)),
^

.../app/test-pmd/cmdline_flow.c(1599): error #188: enumerated type mixed with another type

                ctx->next[ctx->next_num - 2] = NEXT_ENTRY(PREFIX);
                                               ^

.../app/test-pmd/cmdline_flow.c(1664): error #83: type qualifier specified more than once

        static const enum index const next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
                                ^

.../app/test-pmd/cmdline_flow.c(1664): error #188: enumerated type mixed with another type

        static const enum index const next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
                                               ^

.../app/test-pmd/cmdline_flow.c(2302): error #188: enumerated type mixed with another type

        ctx->curr = 0;
                  ^

.../app/test-pmd/cmdline_flow.c(2303): error #188: enumerated type mixed with another type

        ctx->prev = 0;
                  ^
Signature · 2 lines
Adrien Mazarguil
6WIND
#111 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

Quoted 108 lines · Adrien Mazarguil

Hi Ferruh,

Quoted 105 lines · Ferruh Yigit

Hi Adrien,

Quoted 80 lines · Adrien Mazarguil

As previously discussed in RFC v1 [1], RFC v2 [2], with changes described in [3] (also pasted below), here is the first non-draft series for this new API.

Its capabilities are so generic that its name had to be vague, it may be called "Generic flow API", "Generic flow interface" (possibly shortened as "GFI") to refer to the name of the new filter type, or "rte_flow" from the prefix used for its public symbols. I personally favor the latter.

While it is currently meant to supersede existing filter types in order for all PMDs to expose a common filtering/classification interface, it may eventually evolve to cover the following ideas as well:

- Rx/Tx offloads configuration through automatic offloads for specific packets, e.g. performing checksum on TCP packets could be expressed with an egress rule with a TCP pattern and a kind of checksum action.

- RSS configuration (already defined actually). Could be global or per rule depending on hardware capabilities.

- Switching configuration for devices with many physical ports; rules doing both ingress and egress could even be used to completely bypass software if supported by hardware.

[1] http://dpdk.org/ml/archives/dev/2016-July/043365.html
[2] http://dpdk.org/ml/archives/dev/2016-August/045383.html
[3] http://dpdk.org/ml/archives/dev/2016-November/050044.html

Changes since RFC v2:

- New separate VLAN pattern item (previously part of the ETH definition), found to be much more convenient.

- Removed useless "any" field from VF pattern item, the same effect can be achieved by not providing a specification structure.

- Replaced bit-fields from the VXLAN pattern item to avoid endianness conversion issues on 24-bit fields.

- Updated struct rte_flow_item with a new "last" field to create inclusive ranges. They are defined as the interval between (spec & mask) and (last & mask). All three parameters are optional.

- Renamed ID action MARK.

- Renamed "queue" fields in actions QUEUE and DUP to "index".

- "rss_conf" field in RSS action is now const.

- VF action now uses a 32 bit ID like its pattern item counterpart.

- Removed redundant struct rte_flow_pattern, API functions now expect struct
rte_flow_item lists terminated by END items.

- Replaced struct rte_flow_actions for the same reason, with struct rte_flow_action lists terminated by END actions.

- Error types (enum rte_flow_error_type) have been updated and the cause pointer in struct rte_flow_error is now const.

- Function prototypes (rte_flow_create, rte_flow_validate) have also been updated for clarity.

Additions:

- Public wrapper functions rte_flow_{validate|create|destroy|flush|query} are now implemented in rte_flow.c, with their symbols exported and versioned. Related filter type RTE_ETH_FILTER_GENERIC has been added.

- A separate header (rte_flow_driver.h) has been added for driver-side functionality, in particular struct rte_flow_ops which contains PMD callbacks returned by RTE_ETH_FILTER_GENERIC query.

- testpmd now exposes most of this API through the new "flow" command.

What remains to be done:

- Using endian-aware integer types (rte_beX_t) where necessary for clarity.

Quoted 9 lines

- API documentation (based on RFC).

- testpmd flow command documentation (although context-aware command completion should already help quite a bit in this regard).

- A few pattern item / action properties cannot be configured yet (e.g. rss_conf parameter for RSS action) and a few completions (e.g. possible queue IDs) should be added.

<...>

I was trying to check driver filter API patches, but hit a few compiler errors with this patchset.

[1] clang complains about variable bitfield value changed from -1 to 1. Which is correct, but I guess that is intentional, but I don't know how to tell this to clang?

[2] shred library compilation error, because of missing rte_flow_flush in rte_ether_version.map file

Quoted 6 lines
Quoted 2 lines

[3] bunch of icc compilation errors, almost all are same type:
error #188: enumerated type mixed with another type

Thanks for the report, I'll attempt to address them all in v2. However icc error #188 looks like a pain, I think I can work around it but do we really not tolerate the use of normal integers inside enum fields in DPDK?

If this warning is not improving the code, and community agree on it, it is possible to disable warning by adding "-wd188" to test-pmd Makefile for ICC compiler.

#112 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

In reply to Adrien Mazarguil
Quoted 105 lines · Adrien Mazarguil

Hi Ferruh,

Quoted 102 lines · Ferruh Yigit

Hi Adrien,

Quoted 80 lines · Adrien Mazarguil

As previously discussed in RFC v1 [1], RFC v2 [2], with changes described in [3] (also pasted below), here is the first non-draft series for this new API.

Its capabilities are so generic that its name had to be vague, it may be called "Generic flow API", "Generic flow interface" (possibly shortened as "GFI") to refer to the name of the new filter type, or "rte_flow" from the prefix used for its public symbols. I personally favor the latter.

While it is currently meant to supersede existing filter types in order for all PMDs to expose a common filtering/classification interface, it may eventually evolve to cover the following ideas as well:

- Rx/Tx offloads configuration through automatic offloads for specific packets, e.g. performing checksum on TCP packets could be expressed with an egress rule with a TCP pattern and a kind of checksum action.

- RSS configuration (already defined actually). Could be global or per rule depending on hardware capabilities.

- Switching configuration for devices with many physical ports; rules doing both ingress and egress could even be used to completely bypass software if supported by hardware.

[1] http://dpdk.org/ml/archives/dev/2016-July/043365.html
[2] http://dpdk.org/ml/archives/dev/2016-August/045383.html
[3] http://dpdk.org/ml/archives/dev/2016-November/050044.html

Changes since RFC v2:

- New separate VLAN pattern item (previously part of the ETH definition), found to be much more convenient.

- Removed useless "any" field from VF pattern item, the same effect can be achieved by not providing a specification structure.

- Replaced bit-fields from the VXLAN pattern item to avoid endianness conversion issues on 24-bit fields.

- Updated struct rte_flow_item with a new "last" field to create inclusive ranges. They are defined as the interval between (spec & mask) and (last & mask). All three parameters are optional.

- Renamed ID action MARK.

- Renamed "queue" fields in actions QUEUE and DUP to "index".

- "rss_conf" field in RSS action is now const.

- VF action now uses a 32 bit ID like its pattern item counterpart.

- Removed redundant struct rte_flow_pattern, API functions now expect struct
rte_flow_item lists terminated by END items.

- Replaced struct rte_flow_actions for the same reason, with struct rte_flow_action lists terminated by END actions.

- Error types (enum rte_flow_error_type) have been updated and the cause pointer in struct rte_flow_error is now const.

- Function prototypes (rte_flow_create, rte_flow_validate) have also been updated for clarity.

Additions:

- Public wrapper functions rte_flow_{validate|create|destroy|flush|query} are now implemented in rte_flow.c, with their symbols exported and versioned. Related filter type RTE_ETH_FILTER_GENERIC has been added.

- A separate header (rte_flow_driver.h) has been added for driver-side functionality, in particular struct rte_flow_ops which contains PMD callbacks returned by RTE_ETH_FILTER_GENERIC query.

- testpmd now exposes most of this API through the new "flow" command.

What remains to be done:

- Using endian-aware integer types (rte_beX_t) where necessary for clarity.

Quoted 9 lines

- API documentation (based on RFC).

- testpmd flow command documentation (although context-aware command completion should already help quite a bit in this regard).

- A few pattern item / action properties cannot be configured yet (e.g. rss_conf parameter for RSS action) and a few completions (e.g. possible queue IDs) should be added.

<...>

I was trying to check driver filter API patches, but hit a few compiler errors with this patchset.

[1] clang complains about variable bitfield value changed from -1 to 1. Which is correct, but I guess that is intentional, but I don't know how to tell this to clang?

Quoted 7 lines
Quoted 5 lines

[2] shred library compilation error, because of missing rte_flow_flush in rte_ether_version.map file

[3] bunch of icc compilation errors, almost all are same type:
error #188: enumerated type mixed with another type

Thanks for the report, I'll attempt to address them all in v2.

Hi Adrien,

I would like to remind that there are driver patch sets depends to this patch.

New version of this patch should give some time to drivers to re-do (if required) the patchsets before integration deadline.

Thanks,
ferruh

Quoted 4 lines

However icc
error #188 looks like a pain, I think I can work around it but do we really not tolerate the use of normal integers inside enum fields in DPDK?

<...>

#113 lore raw

Re: [PATCH 00/22] Generic flow API (rte_flow)

Quoted 114 lines · Ferruh Yigit
Quoted 105 lines · Adrien Mazarguil

Hi Ferruh,

Quoted 102 lines · Ferruh Yigit

Hi Adrien,

Quoted 80 lines · Adrien Mazarguil

As previously discussed in RFC v1 [1], RFC v2 [2], with changes described in [3] (also pasted below), here is the first non-draft series for this new API.

Its capabilities are so generic that its name had to be vague, it may be called "Generic flow API", "Generic flow interface" (possibly shortened as "GFI") to refer to the name of the new filter type, or "rte_flow" from the prefix used for its public symbols. I personally favor the latter.

While it is currently meant to supersede existing filter types in order for all PMDs to expose a common filtering/classification interface, it may eventually evolve to cover the following ideas as well:

- Rx/Tx offloads configuration through automatic offloads for specific packets, e.g. performing checksum on TCP packets could be expressed with an egress rule with a TCP pattern and a kind of checksum action.

- RSS configuration (already defined actually). Could be global or per rule depending on hardware capabilities.

- Switching configuration for devices with many physical ports; rules doing both ingress and egress could even be used to completely bypass software if supported by hardware.

[1] http://dpdk.org/ml/archives/dev/2016-July/043365.html
[2] http://dpdk.org/ml/archives/dev/2016-August/045383.html
[3] http://dpdk.org/ml/archives/dev/2016-November/050044.html

Changes since RFC v2:

- New separate VLAN pattern item (previously part of the ETH definition), found to be much more convenient.

- Removed useless "any" field from VF pattern item, the same effect can be achieved by not providing a specification structure.

- Replaced bit-fields from the VXLAN pattern item to avoid endianness conversion issues on 24-bit fields.

- Updated struct rte_flow_item with a new "last" field to create inclusive ranges. They are defined as the interval between (spec & mask) and (last & mask). All three parameters are optional.

- Renamed ID action MARK.

- Renamed "queue" fields in actions QUEUE and DUP to "index".

- "rss_conf" field in RSS action is now const.

- VF action now uses a 32 bit ID like its pattern item counterpart.

- Removed redundant struct rte_flow_pattern, API functions now expect struct
rte_flow_item lists terminated by END items.

- Replaced struct rte_flow_actions for the same reason, with struct rte_flow_action lists terminated by END actions.

- Error types (enum rte_flow_error_type) have been updated and the cause pointer in struct rte_flow_error is now const.

- Function prototypes (rte_flow_create, rte_flow_validate) have also been updated for clarity.

Additions:

- Public wrapper functions rte_flow_{validate|create|destroy|flush|query} are now implemented in rte_flow.c, with their symbols exported and versioned. Related filter type RTE_ETH_FILTER_GENERIC has been added.

- A separate header (rte_flow_driver.h) has been added for driver-side functionality, in particular struct rte_flow_ops which contains PMD callbacks returned by RTE_ETH_FILTER_GENERIC query.

- testpmd now exposes most of this API through the new "flow" command.

What remains to be done:

- Using endian-aware integer types (rte_beX_t) where necessary for clarity.

Quoted 9 lines

- API documentation (based on RFC).

- testpmd flow command documentation (although context-aware command completion should already help quite a bit in this regard).

- A few pattern item / action properties cannot be configured yet (e.g. rss_conf parameter for RSS action) and a few completions (e.g. possible queue IDs) should be added.

<...>

I was trying to check driver filter API patches, but hit a few compiler errors with this patchset.

[1] clang complains about variable bitfield value changed from -1 to 1. Which is correct, but I guess that is intentional, but I don't know how to tell this to clang?

Quoted 7 lines
Quoted 5 lines

[2] shred library compilation error, because of missing rte_flow_flush in rte_ether_version.map file

[3] bunch of icc compilation errors, almost all are same type:
error #188: enumerated type mixed with another type

Thanks for the report, I'll attempt to address them all in v2.

Quoted 7 lines

Hi Adrien,

I would like to remind that there are driver patch sets depends to this patch.

New version of this patch should give some time to drivers to re-do (if required) the patchsets before integration deadline.

Hi Ferruh,

I intend to send v2 (including all the requested changes and fixes) shortly, most likely today.

Signature · 2 lines
Adrien Mazarguil
6WIND
#114 lore raw

v2 cover Generic flow API (rte_flow)

In reply to Adrien Mazarguil

As previously discussed in RFC v1 [1], RFC v2 [2], with changes described in [3] (also pasted below), here is the first non-draft series for this new API.

Its capabilities are so generic that its name had to be vague, it may be called "Generic flow API", "Generic flow interface" (possibly shortened as "GFI") to refer to the name of the new filter type, or "rte_flow" from the prefix used for its public symbols. I personally favor the latter.

While it is currently meant to supersede existing filter types in order for all PMDs to expose a common filtering/classification interface, it may eventually evolve to cover the following ideas as well:

- Rx/Tx offloads configuration through automatic offloads for specific packets, e.g. performing checksum on TCP packets could be expressed with an egress rule with a TCP pattern and a kind of checksum action.

- RSS configuration (already defined actually). Could be global or per rule depending on hardware capabilities.

- Switching configuration for devices with many physical ports; rules doing both ingress and egress could even be used to completely bypass software if supported by hardware.

[1] http://dpdk.org/ml/archives/dev/2016-July/043365.html
[2] http://dpdk.org/ml/archives/dev/2016-August/045383.html
[3] http://dpdk.org/ml/archives/dev/2016-November/050044.html

Changes since v1 series:

- Added programmer's guide documentation for rte_flow.

- Added depreciation notice for the legacy API.

- Documented testpmd flow command.

- Fixed missing rte_flow_flush symbol in rte_ether_version.map.

- Cleaned up API documentation in rte_flow.h.

- Replaced "min/max" parameters with "num" in struct rte_flow_item_any, to align behavior with other item definitions.

- Fixed "type" (EtherType) size in struct rte_flow_item_eth.

- Renamed "queues" to "num" in struct rte_flow_action_rss.

- Fixed missing const in rte_flow_error_set() prototype definition.

- Fixed testpmd flow create command that did not save the rte_flow object pointer, causing crashes.

- Hopefully fixed all the remaining ICC/clang errors.

- Replaced testpmd flow command's "fix" token with "is" for clarity.

Changes since RFC v2:

- New separate VLAN pattern item (previously part of the ETH definition), found to be much more convenient.

- Removed useless "any" field from VF pattern item, the same effect can be achieved by not providing a specification structure.

- Replaced bit-fields from the VXLAN pattern item to avoid endianness conversion issues on 24-bit fields.

- Updated struct rte_flow_item with a new "last" field to create inclusive ranges. They are defined as the interval between (spec & mask) and (last & mask). All three parameters are optional.

- Renamed ID action MARK.

- Renamed "queue" fields in actions QUEUE and DUP to "index".

- "rss_conf" field in RSS action is now const.

- VF action now uses a 32 bit ID like its pattern item counterpart.

- Removed redundant struct rte_flow_pattern, API functions now expect struct
rte_flow_item lists terminated by END items.

- Replaced struct rte_flow_actions for the same reason, with struct rte_flow_action lists terminated by END actions.

- Error types (enum rte_flow_error_type) have been updated and the cause pointer in struct rte_flow_error is now const.

- Function prototypes (rte_flow_create, rte_flow_validate) have also been updated for clarity.

Additions:

- Public wrapper functions rte_flow_{validate|create|destroy|flush|query} are now implemented in rte_flow.c, with their symbols exported and versioned. Related filter type RTE_ETH_FILTER_GENERIC has been added.

- A separate header (rte_flow_driver.h) has been added for driver-side functionality, in particular struct rte_flow_ops which contains PMD callbacks returned by RTE_ETH_FILTER_GENERIC query.

- testpmd now exposes most of this API through the new "flow" command.

What remains to be done:

- Using endian-aware integer types (rte_beX_t) where necessary for clarity.

- API documentation (based on RFC).

- testpmd flow command documentation (although context-aware command completion should already help quite a bit in this regard).

- A few pattern item / action properties cannot be configured yet (e.g. rss_conf parameter for RSS action) and a few completions (e.g. possible queue IDs) should be added.

Adrien Mazarguil (25):
ethdev: introduce generic flow API
doc: add rte_flow prog guide
doc: announce depreciation of legacy filter types
cmdline: add support for dynamic tokens
cmdline: add alignment constraint app/testpmd: implement basic support for rte_flow app/testpmd: add flow command app/testpmd: add rte_flow integer support app/testpmd: add flow list command app/testpmd: add flow flush command app/testpmd: add flow destroy command app/testpmd: add flow validate/create commands app/testpmd: add flow query command app/testpmd: add rte_flow item spec handler app/testpmd: add rte_flow item spec prefix length app/testpmd: add rte_flow bit-field support app/testpmd: add item any to flow command app/testpmd: add various items to flow command app/testpmd: add item raw to flow command app/testpmd: add items eth/vlan to flow command app/testpmd: add items ipv4/ipv6 to flow command app/testpmd: add L4 items to flow command app/testpmd: add various actions to flow command app/testpmd: add queue actions to flow command
doc: describe testpmd flow command

 MAINTAINERS                                 |    4 +
 app/test-pmd/Makefile                       |    1 +
 app/test-pmd/cmdline.c                      |   32 +
 app/test-pmd/cmdline_flow.c                 | 2575 ++++++++++++++++++++++
 app/test-pmd/config.c                       |  485 ++++
 app/test-pmd/csumonly.c                     |    1 +
 app/test-pmd/flowgen.c                      |    1 +
 app/test-pmd/icmpecho.c                     |    1 +
 app/test-pmd/ieee1588fwd.c                  |    1 +
 app/test-pmd/iofwd.c                        |    1 +
 app/test-pmd/macfwd.c                       |    1 +
 app/test-pmd/macswap.c                      |    1 +
 app/test-pmd/parameters.c                   |    1 +
 app/test-pmd/rxonly.c                       |    1 +
 app/test-pmd/testpmd.c                      |    6 +
 app/test-pmd/testpmd.h                      |   27 +
 app/test-pmd/txonly.c                       |    1 +
 doc/api/doxy-api-index.md                   |    2 +
 doc/guides/prog_guide/index.rst             |    1 +
 doc/guides/prog_guide/rte_flow.rst          | 1853 ++++++++++++++++
 doc/guides/rel_notes/deprecation.rst        |    7 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  612 +++++
 lib/librte_cmdline/cmdline_parse.c          |   67 +-
 lib/librte_cmdline/cmdline_parse.h          |   21 +
 lib/librte_ether/Makefile                   |    3 +
 lib/librte_ether/rte_eth_ctrl.h             |    1 +
 lib/librte_ether/rte_ether_version.map      |   11 +
 lib/librte_ether/rte_flow.c                 |  159 ++
 lib/librte_ether/rte_flow.h                 |  942 ++++++++
 lib/librte_ether/rte_flow_driver.h          |  181 ++
 30 files changed, 6991 insertions(+), 9 deletions(-)
 create mode 100644 app/test-pmd/cmdline_flow.c
 create mode 100644 doc/guides/prog_guide/rte_flow.rst
 create mode 100644 lib/librte_ether/rte_flow.c
 create mode 100644 lib/librte_ether/rte_flow.h
 create mode 100644 lib/librte_ether/rte_flow_driver.h
Signature · 1 line
2.1.4
#115 lore raw

PATCH v2 1/25 ethdev: introduce generic flow API

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

---
 MAINTAINERS                            |   4 +
 doc/api/doxy-api-index.md              |   2 +
 lib/librte_ether/Makefile              |   3 +
 lib/librte_ether/rte_eth_ctrl.h        |   1 +
 lib/librte_ether/rte_ether_version.map |  11 +
 lib/librte_ether/rte_flow.c            | 159 +++++
 lib/librte_ether/rte_flow.h            | 942 ++++++++++++++++++++++++++++
 lib/librte_ether/rte_flow_driver.h     | 181 ++++++
 8 files changed, 1303 insertions(+)
MAINTAINERS +4 -0
index 26d9590..5975cff 100644
@@ -243,6 +243,10 @@ M: Thomas Monjalon <[email protected]>
 F: lib/librte_ether/
 F: scripts/test-null.sh
 
+Generic flow API
+M: Adrien Mazarguil <[email protected]>
+F: lib/librte_ether/rte_flow*
+
 Crypto API
 M: Declan Doherty <[email protected]>
 F: lib/librte_cryptodev/
doc/api/doxy-api-index.md +2 -0
index de65b4c..4951552 100644
@@ -39,6 +39,8 @@ There are many libraries, so their headers may be grouped by topics:
   [dev]                (@ref rte_dev.h),
   [ethdev]             (@ref rte_ethdev.h),
   [ethctrl]            (@ref rte_eth_ctrl.h),
+  [rte_flow]           (@ref rte_flow.h),
+  [rte_flow_driver]    (@ref rte_flow_driver.h),
   [cryptodev]          (@ref rte_cryptodev.h),
   [devargs]            (@ref rte_devargs.h),
   [bond]               (@ref rte_eth_bond.h),
lib/librte_ether/Makefile +3 -0
index efe1e5f..9335361 100644
@@ -44,6 +44,7 @@ EXPORT_MAP := rte_ether_version.map
 LIBABIVER := 5
 
 SRCS-y += rte_ethdev.c
+SRCS-y += rte_flow.c
 
 #
 # Export include files
@@ -51,6 +52,8 @@ SRCS-y += rte_ethdev.c
 SYMLINK-y-include += rte_ethdev.h
 SYMLINK-y-include += rte_eth_ctrl.h
 SYMLINK-y-include += rte_dev_info.h
+SYMLINK-y-include += rte_flow.h
+SYMLINK-y-include += rte_flow_driver.h
 
 # this lib depends upon:
 DEPDIRS-y += lib/librte_net lib/librte_eal lib/librte_mempool lib/librte_ring lib/librte_mbuf
lib/librte_ether/rte_eth_ctrl.h +1 -0
index fe80eb0..8386904 100644
@@ -99,6 +99,7 @@ enum rte_filter_type {
 	RTE_ETH_FILTER_FDIR,
 	RTE_ETH_FILTER_HASH,
 	RTE_ETH_FILTER_L2_TUNNEL,
+	RTE_ETH_FILTER_GENERIC,
 	RTE_ETH_FILTER_MAX
 };
 
lib/librte_ether/rte_ether_version.map +11 -0
index 72be66d..384cdee 100644
@@ -147,3 +147,14 @@ DPDK_16.11 {
 	rte_eth_dev_pci_remove;
 
 } DPDK_16.07;
+
+DPDK_17.02 {
+	global:
+
+	rte_flow_validate;
+	rte_flow_create;
+	rte_flow_destroy;
+	rte_flow_flush;
+	rte_flow_query;
+
+} DPDK_16.11;
lib/librte_ether/rte_flow.c new file +159 -0

159 lines not on this page · raw message

lib/librte_ether/rte_flow.h new file +942 -0

942 lines not on this page · raw message

lib/librte_ether/rte_flow_driver.h new file +181 -0

181 lines not on this page · raw message

Signature · 1 line
2.1.4
#116 lore raw

Re: [PATCH v2 01/25] ethdev: introduce generic flow API

Hi, Adrien

Quoted 42 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Saturday, December 17, 2016 12:25 AM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 01/25] ethdev: introduce generic flow API

This new API supersedes all the legacy filter types described in rte_eth_ctrl.h. It is slightly higher level and as a result relies more on PMDs to process and validate flow rules.

Benefits:

- A unified API is easier to program for, applications do not have to be written for a specific filter type which may or may not be supported by the underlying device.

- The behavior of a flow rule is the same regardless of the underlying device, applications do not need to be aware of hardware quirks.

- Extensible by design, API/ABI breakage should rarely occur if at all.

- Documentation is self-standing, no need to look up elsewhere.

Existing filter types will be deprecated and removed in the near future.

---
 MAINTAINERS                            |   4 +
 doc/api/doxy-api-index.md              |   2 +
 lib/librte_ether/Makefile              |   3 +
 lib/librte_ether/rte_eth_ctrl.h        |   1 +
 lib/librte_ether/rte_ether_version.map |  11 +
 lib/librte_ether/rte_flow.c            | 159 +++++
 lib/librte_ether/rte_flow.h            | 942 ++++++++++++++++++++++++++++
 lib/librte_ether/rte_flow_driver.h     | 181 ++++++
 8 files changed, 1303 insertions(+)
MAINTAINERS +0 -0
index 26d9590..5975cff 100644
@@ -243,6 +243,10 @@ M: Thomas Monjalon
Quoted 4 lines

<[email protected]>
F: lib/librte_ether/
F: scripts/test-null.sh

+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets to
+ * spread them among several queues according to the provided parameters.
+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf field,
+ * however it conflicts with the MARK action as they share the same
+ * space. When both actions are specified, the RSS hash is discarded +and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_rss {
+ const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+ uint16_t num; /**< Number of entries in queue[]. */
+ uint16_t queue[]; /**< Queues indices to use. */ };
+

I am plan for moving rss to rte_flow.
May I ask you some question for this struct of rte_flow_action_rss.
1. why do you use pointer mode for rss_conf, why not use " struct rte_eth_rss_conf rss_conf "?
we need to fill these rss info which get from CLI to this struct, if we use the pointer mode, how can we fill in these info?

2. And also why the" const" is not need? We need a const ?How can we config this parameter?

3. what is your expect mode for CLI command for rss config? When I type in :
" flow create 0 pattern eth / tcp / end actions rss queues queue 0 /end " Or " flow create 0 pattern eth / tcp / end actions rss queues {0 1 2} /end " I get " Bad arguments ".

So, the right CLI command is ?

Thank you!

#117 lore raw

Re: [PATCH v2 01/25] ethdev: introduce generic flow API

Hi Wei,

Sorry for the late answer, see below.

On Mon, Oct 23, 2017 at 08:53:49AM +0000, Zhao1, Wei wrote:
<snip>

Quoted 21 lines
lib/librte_ether/rte_flow.h new file +20 -0
+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets to
+ * spread them among several queues according to the provided parameters.
+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf field,
+ * however it conflicts with the MARK action as they share the same
+ * space. When both actions are specified, the RSS hash is discarded 
+and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_rss {
+	const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+	uint16_t num; /**< Number of entries in queue[]. */
+	uint16_t queue[]; /**< Queues indices to use. */ };
+
Quoted 4 lines

I am plan for moving rss to rte_flow.
May I ask you some question for this struct of rte_flow_action_rss.
1. why do you use pointer mode for rss_conf, why not use " struct rte_eth_rss_conf rss_conf "?
we need to fill these rss info which get from CLI to this struct, if we use the pointer mode, how can we fill in these info?

The original idea was to provide the ability to share a single rss_conf structure between many flow rules and avoid redundant allocations.

It's based on the fact applications currently use a single RSS context for everything, they can easily make rss_conf point the global context found in struct rte_eth_dev.

Currently the testpmd flow command is incomplete, it doesn't support the configuration of this field and always provides NULL to PMDs instead. This is not documented in the flow API and may crash PMDs.

For the time being, a PMD can interpret NULL as a kind of default global rss_conf parameters, as is done in both mlx5 and mlx4 PMDs.

That's a problem that needs to be addressed in testpmd.

Quoted 1 line

2. And also why the" const" is not need? We need a const ?How can we config this parameter?

It means the allocation is managed outside of rte_flow, where the data may or may not be const, it's up to the application. The pointer stored in this structure is only a copy.

Whether the pointed data remains allocated once the flow rule is created is unspecified. A PMD cannot assume anything and has to copy these parameters if needed later.

It's an API issue I'd like to address by embedding the rss_conf parameters directly in rte_flow_action_rss instead of doing so through a pointer.

Quoted 6 lines

3. what is your expect mode for CLI command for rss config? When I type in :
" flow create 0 pattern eth / tcp / end actions rss queues queue 0 /end " Or " flow create 0 pattern eth / tcp / end actions rss queues {0 1 2} /end " I get " Bad arguments ".

So, the right CLI command is ?

Basically you need to specify an additional "end" keyword to terminate the queues list (tab completion shows it):

flow create 0 ingress pattern eth / tcp / end actions rss queues 0 1 2 end / end

There are other design flaws with the RSS action definition:

- RSS hash algorithm to use is missing, PMDs must rely on global parameters.
- The C99-style flexible queues array is a super pain to manage and is not supported by C++. I want to make it a normal pointer (the same applies to the RAW pattern item).

These changes are planned for the next overhaul of rte_flow, I'll submit a RFC for them as soon as I get the chance.

Signature · 2 lines
Adrien Mazarguil
6WIND
#118 lore raw

Re: [PATCH v2 01/25] ethdev: introduce generic flow API

Hi, Adrien

Thank you for your so details answer!
Let me study your mail first then maybe I will supply some details when I implement PMD code of MOVING rss to rte_flow for ixgbe.

Quoted 102 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Wednesday, November 1, 2017 1:46 AM
To: Zhao1, Wei <[email protected]>
Cc: [email protected]
Subject: Re: [dpdk-dev] [PATCH v2 01/25] ethdev: introduce generic flow API

Hi Wei,

Sorry for the late answer, see below.

On Mon, Oct 23, 2017 at 08:53:49AM +0000, Zhao1, Wei wrote:
<snip>

lib/librte_ether/rte_flow.h

Quoted 6 lines
lib/librte_ether/rte_flow.h new file +6 -0
+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets
+to
+ * spread them among several queues according to the provided

parameters.

Quoted 18 lines

+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf

lib/librte_ether/rte_flow.h new file +15 -0
+field,
+ * however it conflicts with the MARK action as they share the same
+ * space. When both actions are specified, the RSS hash is discarded
+and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The
+mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_rss {
+	const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+	uint16_t num; /**< Number of entries in queue[]. */
+	uint16_t queue[]; /**< Queues indices to use. */ };
+
Quoted 3 lines

I am plan for moving rss to rte_flow.
May I ask you some question for this struct of rte_flow_action_rss.
1. why do you use pointer mode for rss_conf, why not use " struct

rte_eth_rss_conf rss_conf "?

Quoted 1 line

we need to fill these rss info which get from CLI to this struct, if we use the

pointer mode, how can we fill in these info?

The original idea was to provide the ability to share a single rss_conf structure between many flow rules and avoid redundant allocations.

It's based on the fact applications currently use a single RSS context for everything, they can easily make rss_conf point the global context found in struct rte_eth_dev.

Currently the testpmd flow command is incomplete, it doesn't support the configuration of this field and always provides NULL to PMDs instead. This is not documented in the flow API and may crash PMDs.

For the time being, a PMD can interpret NULL as a kind of default global rss_conf parameters, as is done in both mlx5 and mlx4 PMDs.

That's a problem that needs to be addressed in testpmd.

Quoted 1 line

2. And also why the" const" is not need? We need a const ?How can we

config this parameter?

It means the allocation is managed outside of rte_flow, where the data may or may not be const, it's up to the application. The pointer stored in this structure is only a copy.

Whether the pointed data remains allocated once the flow rule is created is unspecified. A PMD cannot assume anything and has to copy these parameters if needed later.

It's an API issue I'd like to address by embedding the rss_conf parameters directly in rte_flow_action_rss instead of doing so through a pointer.

Quoted 1 line

3. what is your expect mode for CLI command for rss config? When I type

in :

Quoted 5 lines

" flow create 0 pattern eth / tcp / end actions rss queues queue 0 /end " Or " flow create 0 pattern eth / tcp / end actions rss queues {0 1 2} /end " I get " Bad arguments ".

So, the right CLI command is ?

Basically you need to specify an additional "end" keyword to terminate the queues list (tab completion shows it):

flow create 0 ingress pattern eth / tcp / end actions rss queues 0 1 2 end /
end

There are other design flaws with the RSS action definition:

- RSS hash algorithm to use is missing, PMDs must rely on global parameters.
- The C99-style flexible queues array is a super pain to manage and is not supported by C++. I want to make it a normal pointer (the same applies to the RAW pattern item).

These changes are planned for the next overhaul of rte_flow, I'll submit a RFC for them as soon as I get the chance.

--
Adrien Mazarguil
6WIND

#119 lore raw

Re: [PATCH v2 01/25] ethdev: introduce generic flow API

In reply to Adrien Mazarguil

Hi,Adrien

Quoted 52 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Wednesday, November 1, 2017 1:46 AM
To: Zhao1, Wei <[email protected]>
Cc: [email protected]
Subject: Re: [dpdk-dev] [PATCH v2 01/25] ethdev: introduce generic flow API

Hi Wei,

Sorry for the late answer, see below.

On Mon, Oct 23, 2017 at 08:53:49AM +0000, Zhao1, Wei wrote:
<snip>

lib/librte_ether/rte_flow.h

Quoted 6 lines
lib/librte_ether/rte_flow.h new file +6 -0
+/**
+ * RTE_FLOW_ACTION_TYPE_RSS
+ *
+ * Similar to QUEUE, except RSS is additionally performed on packets
+to
+ * spread them among several queues according to the provided

parameters.

Quoted 18 lines

+ *
+ * Note: RSS hash result is normally stored in the hash.rss mbuf

lib/librte_ether/rte_flow.h new file +15 -0
+field,
+ * however it conflicts with the MARK action as they share the same
+ * space. When both actions are specified, the RSS hash is discarded
+and
+ * PKT_RX_RSS_HASH is not set in ol_flags. MARK has priority. The
+mbuf
+ * structure should eventually evolve to store both.
+ *
+ * Terminating by default.
+ */
+struct rte_flow_action_rss {
+	const struct rte_eth_rss_conf *rss_conf; /**< RSS parameters. */
+	uint16_t num; /**< Number of entries in queue[]. */
+	uint16_t queue[]; /**< Queues indices to use. */ };
+
Quoted 3 lines

I am plan for moving rss to rte_flow.
May I ask you some question for this struct of rte_flow_action_rss.
1. why do you use pointer mode for rss_conf, why not use " struct

rte_eth_rss_conf rss_conf "?

Quoted 1 line

we need to fill these rss info which get from CLI to this struct, if we use the

pointer mode, how can we fill in these info?

The original idea was to provide the ability to share a single rss_conf structure between many flow rules and avoid redundant allocations.

It's based on the fact applications currently use a single RSS context for everything, they can easily make rss_conf point the global context found in struct rte_eth_dev.

Quoted 8 lines

Currently the testpmd flow command is incomplete, it doesn't support the configuration of this field and always provides NULL to PMDs instead. This is not documented in the flow API and may crash PMDs.

For the time being, a PMD can interpret NULL as a kind of default global rss_conf parameters, as is done in both mlx5 and mlx4 PMDs.

That's a problem that needs to be addressed in testpmd.

Yes, this is a block when I test my code of moving rss to rte_flow.
Now, I have do ixgbe rte_flow action parser for RTE_FLOW_ACTION_TYPE_RSS,
but I can not config rss_conf structure member with testpmd,
so I have to use an extra function to config these action rss parameter, Maybe I
have to use this way to test this.
I will commit this new code of moving rss to rte_flow later even if testpmd not support this test, like mlx4 and mlx5. Do you have other more suggestion for test this feature?

Thank you.

Quoted 7 lines
Quoted 1 line

2. And also why the" const" is not need? We need a const ?How can we

config this parameter?

It means the allocation is managed outside of rte_flow, where the data may or may not be const, it's up to the application. The pointer stored in this structure is only a copy.

Got.

Quoted 7 lines

Whether the pointed data remains allocated once the flow rule is created is unspecified. A PMD cannot assume anything and has to copy these parameters if needed later.

It's an API issue I'd like to address by embedding the rss_conf parameters directly in rte_flow_action_rss instead of doing so through a pointer.

Yes, I support your idea for this, but by now PMD code not only IXGBE but also MLX4 and MLX5 have Implement PMD code on the assumption that it is a pointer.

Quoted 6 lines
Quoted 1 line

3. what is your expect mode for CLI command for rss config? When I type

in :

Quoted 4 lines

" flow create 0 pattern eth / tcp / end actions rss queues queue 0 /end " Or " flow create 0 pattern eth / tcp / end actions rss queues {0 1 2} /end " I get " Bad arguments ".

Quoted 8 lines
Quoted 1 line

So, the right CLI command is ?

Basically you need to specify an additional "end" keyword to terminate the queues list (tab completion shows it):

flow create 0 ingress pattern eth / tcp / end actions rss queues 0 1 2 end /
end

Ok.

Quoted 9 lines

There are other design flaws with the RSS action definition:

- RSS hash algorithm to use is missing, PMDs must rely on global parameters.
- The C99-style flexible queues array is a super pain to manage and is not supported by C++. I want to make it a normal pointer (the same applies to the RAW pattern item).

These changes are planned for the next overhaul of rte_flow, I'll submit a RFC for them as soon as I get the chance.

Good idea. wait for you.

Quoted 4 lines

--
Adrien Mazarguil
6WIND

#120 lore raw

PATCH v2 2/25 doc: add rte_flow prog guide

In reply to Adrien Mazarguil

This documentation is based on the latest RFC submission, subsequently updated according to feedback from the community.

---
 doc/guides/prog_guide/index.rst    |    1 +
 doc/guides/prog_guide/rte_flow.rst | 1853 +++++++++++++++++++++++++++++++
 2 files changed, 1854 insertions(+)
doc/guides/prog_guide/index.rst +1 -0
index e5a50a8..ed7f770 100644
@@ -42,6 +42,7 @@ Programmer's Guide
     mempool_lib
     mbuf_lib
     poll_mode_drv
+    rte_flow
     cryptodev_lib
     link_bonding_poll_mode_drv_lib
     timer_lib
doc/guides/prog_guide/rte_flow.rst new file +1853 -0

1,853 lines not on this page · raw message

Signature · 1 line
2.1.4
#121 lore raw

Re: [PATCH v2 02/25] doc: add rte_flow prog guide

Quoted 8 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Friday, December 16, 2016 4:25 PM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 02/25] doc: add rte_flow prog guide

This documentation is based on the latest RFC submission, subsequently updated according to feedback from the community.

Hi,

Thanks. That is a very good doc.

A few RST comments.

Section headers should use the use the following underline formats:

Level 1 Heading
===============

Level 2 Heading
---------------

Level 3 Heading
~~~~~~~~~~~~~~~

Level 4 Heading
^^^^^^^^^^^^^^^

See: http://dpdk.org/doc/guides/contributing/documentation.html#rst-guidelines

Also, some of the section headers for Attributes, Patterns, Match and Action are a bit short and it isn't clear what section you are in, especially in the PDF doc. It might be clearer to add the section name before each item like:

    Attribute: Group
    ~~~~~~~~~~~~~~~~

    Match: VOID
    ~~~~~~~~~~~

Tables should have a reference link and a caption, like this:

    .. _table_qos_pipes:

    .. table:: Sample configuration for QOS pipes.

       +----------+----------+----------+
       | Header 1 | Header 2 | Header 3 |
       |          |          |          |
       +==========+==========+==========+
       | Text     | Text     | Text     |
       +----------+----------+----------+
       | ...      | ...      | ...      |
       +----------+----------+----------+

See: http://dpdk.org/doc/guides/contributing/documentation.html#tables

This will make the tables clearer when there are several in a row and will allow the text to refer to them with :numref:.

Also, there is one typo:

s/unpractically/impractically/

Otherwise, very good work. A good clear document.

John

#122 lore raw

Re: [PATCH v2 02/25] doc: add rte_flow prog guide

Hi John,

Quoted 25 lines · John
Quoted 8 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Friday, December 16, 2016 4:25 PM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 02/25] doc: add rte_flow prog guide

This documentation is based on the latest RFC submission, subsequently updated according to feedback from the community.

Hi,

Thanks. That is a very good doc.

A few RST comments.

Section headers should use the use the following underline formats:

Level 1 Heading
===============

Level 2 Heading
---------------

Quoted 8 lines

Level 3 Heading
~~~~~~~~~~~~~~~

Level 4 Heading
^^^^^^^^^^^^^^^

See: http://dpdk.org/doc/guides/contributing/documentation.html#rst-guidelines

OK, although I do not see any mistake regarding this?

Quoted 33 lines

Also, some of the section headers for Attributes, Patterns, Match and Action are a bit short and it isn't clear what section you are in, especially in the PDF doc. It might be clearer to add the section name before each item like:

    Attribute: Group
    ~~~~~~~~~~~~~~~~

    Match: VOID
    ~~~~~~~~~~~

Tables should have a reference link and a caption, like this:

    .. _table_qos_pipes:

    .. table:: Sample configuration for QOS pipes.

       +----------+----------+----------+
       | Header 1 | Header 2 | Header 3 |
       |          |          |          |
       +==========+==========+==========+
       | Text     | Text     | Text     |
       +----------+----------+----------+
       | ...      | ...      | ...      |
       +----------+----------+----------+

See: http://dpdk.org/doc/guides/contributing/documentation.html#tables

This will make the tables clearer when there are several in a row and will allow the text to refer to them with :numref:.

Quoted 6 lines

Also, there is one typo:

s/unpractically/impractically/

Otherwise, very good work. A good clear document.

Thanks, I will fix these and re-submit.

Signature · 2 lines
Adrien Mazarguil
6WIND
#123 lore raw

PATCH v2 3/25 doc: announce depreciation of legacy filter types

In reply to Adrien Mazarguil

They are superseded by the generic flow API (rte_flow). Target release is not defined yet.

---
 doc/guides/rel_notes/deprecation.rst | 7 +++++++
 1 file changed, 7 insertions(+)
doc/guides/rel_notes/deprecation.rst +7 -0
index 2d17bc6..4819078 100644
@@ -71,3 +71,10 @@ Deprecation Notices
 * mempool: The functions for single/multi producer/consumer are deprecated
   and will be removed in 17.02.
   It is replaced by ``rte_mempool_generic_get/put`` functions.
+
+* ethdev: the legacy filter API, including rte_eth_dev_filter_supported(),
+  rte_eth_dev_filter_ctrl() as well as filter types MACVLAN, ETHERTYPE,
+  FLEXIBLE, SYN, NTUPLE, TUNNEL, FDIR, HASH and L2_TUNNEL, is superseded by
+  the generic flow API (rte_flow) in PMDs that implement the latter.
+  Target release for removal of the legacy API will be defined once most
+  PMDs have switched to rte_flow.
Signature · 1 line
2.1.4
#124 lore raw

Re: [PATCH v2 03/25] doc: announce depreciation of legacy filter types

Quoted 28 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Friday, December 16, 2016 4:25 PM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 03/25] doc: announce depreciation of legacy filter types

They are superseded by the generic flow API (rte_flow). Target release is not defined yet.

---
 doc/guides/rel_notes/deprecation.rst | 7 +++++++
 1 file changed, 7 insertions(+)
doc/guides/rel_notes/deprecation.rst +0 -0
index 2d17bc6..4819078 100644
@@ -71,3 +71,10 @@ Deprecation Notices
 * mempool: The functions for single/multi producer/consumer are

deprecated
and will be removed in 17.02.
It is replaced by ``rte_mempool_generic_get/put`` functions.

doc/guides/rel_notes/deprecation.rst

Quoted 10 lines
doc/guides/rel_notes/deprecation.rst +10 -0
+
+* ethdev: the legacy filter API, including
+rte_eth_dev_filter_supported(),
+  rte_eth_dev_filter_ctrl() as well as filter types MACVLAN, ETHERTYPE,
+  FLEXIBLE, SYN, NTUPLE, TUNNEL, FDIR, HASH and L2_TUNNEL, is
+superseded by
+  the generic flow API (rte_flow) in PMDs that implement the latter.
+  Target release for removal of the legacy API will be defined once
+most
+  PMDs have switched to rte_flow.

All the function names and constants should be fixed width quoted:
``rte_eth_dev_filter_supported()``.

#125 lore raw

PATCH v2 4/25 cmdline: add support for dynamic tokens

In reply to Adrien Mazarguil

Considering tokens must be hard-coded in a list part of the instruction structure, context-dependent tokens cannot be expressed.

This commit adds support for building dynamic token lists through a user-provided function, which is called when the static token list is empty (a single NULL entry).

Because no structures are modified (existing fields are reused), this commit has no impact on the current ABI.

---
 lib/librte_cmdline/cmdline_parse.c | 60 +++++++++++++++++++++++++++++----
 lib/librte_cmdline/cmdline_parse.h | 21 ++++++++++++
 2 files changed, 74 insertions(+), 7 deletions(-)
lib/librte_cmdline/cmdline_parse.c +53 -7
index b496067..14f5553 100644
@@ -146,7 +146,9 @@ nb_common_chars(const char * s1, const char * s2)
  */
 static int
 match_inst(cmdline_parse_inst_t *inst, const char *buf,
-	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
+	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size,
+	   cmdline_parse_token_hdr_t
+		*(*dyn_tokens)[CMDLINE_PARSE_DYNAMIC_TOKENS])
 {
 	unsigned int token_num=0;
 	cmdline_parse_token_hdr_t * token_p;
@@ -155,6 +157,11 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf,
 	struct cmdline_token_hdr token_hdr;
 
 	token_p = inst->tokens[token_num];
+	if (!token_p && dyn_tokens && inst->f) {
+		if (!(*dyn_tokens)[0])
+			inst->f(&(*dyn_tokens)[0], NULL, dyn_tokens);
+		token_p = (*dyn_tokens)[0];
+	}
 	if (token_p)
 		memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
@@ -196,7 +203,17 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf,
 		buf += n;
 
 		token_num ++;
-		token_p = inst->tokens[token_num];
+		if (!inst->tokens[0]) {
+			if (token_num < (CMDLINE_PARSE_DYNAMIC_TOKENS - 1)) {
+				if (!(*dyn_tokens)[token_num])
+					inst->f(&(*dyn_tokens)[token_num],
+						NULL,
+						dyn_tokens);
+				token_p = (*dyn_tokens)[token_num];
+			} else
+				token_p = NULL;
+		} else
+			token_p = inst->tokens[token_num];
 		if (token_p)
 			memcpy(&token_hdr, token_p, sizeof(token_hdr));
 	}
@@ -239,6 +256,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 	cmdline_parse_inst_t *inst;
 	const char *curbuf;
 	char result_buf[CMDLINE_PARSE_RESULT_BUFSIZE];
+	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	void (*f)(void *, struct cmdline *, void *) = NULL;
 	void *data = NULL;
 	int comment = 0;
@@ -255,6 +273,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 		return CMDLINE_PARSE_BAD_ARGS;
 
 	ctx = cl->ctx;
+	memset(&dyn_tokens, 0, sizeof(dyn_tokens));
 
 	/*
 	 * - look if the buffer contains at least one line
@@ -299,7 +318,8 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 		debug_printf("INST %d\n", inst_num);
 
 		/* fully parsed */
-		tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf));
+		tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf),
+				 &dyn_tokens);
 
 		if (tok > 0) /* we matched at least one token */
 			err = CMDLINE_PARSE_BAD_ARGS;
@@ -355,6 +375,7 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 	cmdline_parse_token_hdr_t *token_p;
 	struct cmdline_token_hdr token_hdr;
 	char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
+	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	unsigned int partial_tok_len;
 	int comp_len = -1;
 	int tmp_len = -1;
@@ -374,6 +395,7 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 
 	debug_printf("%s called\n", __func__);
 	memset(&token_hdr, 0, sizeof(token_hdr));
+	memset(&dyn_tokens, 0, sizeof(dyn_tokens));
 
 	/* count the number of complete token to parse */
 	for (i=0 ; buf[i] ; i++) {
@@ -396,11 +418,24 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 		inst = ctx[inst_num];
 		while (inst) {
 			/* parse the first tokens of the inst */
-			if (nb_token && match_inst(inst, buf, nb_token, NULL, 0))
+			if (nb_token &&
+			    match_inst(inst, buf, nb_token, NULL, 0,
+				       &dyn_tokens))
 				goto next;
 
 			debug_printf("instruction match\n");
-			token_p = inst->tokens[nb_token];
+			if (!inst->tokens[0]) {
+				if (nb_token <
+				    (CMDLINE_PARSE_DYNAMIC_TOKENS - 1)) {
+					if (!dyn_tokens[nb_token])
+						inst->f(&dyn_tokens[nb_token],
+							NULL,
+							&dyn_tokens);
+					token_p = dyn_tokens[nb_token];
+				} else
+					token_p = NULL;
+			} else
+				token_p = inst->tokens[nb_token];
 			if (token_p)
 				memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
@@ -490,10 +525,21 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 		/* we need to redo it */
 		inst = ctx[inst_num];
 
-		if (nb_token && match_inst(inst, buf, nb_token, NULL, 0))
+		if (nb_token &&
+		    match_inst(inst, buf, nb_token, NULL, 0, &dyn_tokens))
 			goto next2;
 
-		token_p = inst->tokens[nb_token];
+		if (!inst->tokens[0]) {
+			if (nb_token < (CMDLINE_PARSE_DYNAMIC_TOKENS - 1)) {
+				if (!dyn_tokens[nb_token])
+					inst->f(&dyn_tokens[nb_token],
+						NULL,
+						&dyn_tokens);
+				token_p = dyn_tokens[nb_token];
+			} else
+				token_p = NULL;
+		} else
+			token_p = inst->tokens[nb_token];
 		if (token_p)
 			memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
lib/librte_cmdline/cmdline_parse.h +21 -0
index 4ac05d6..65b18d4 100644
@@ -83,6 +83,9 @@ extern "C" {
 /* maximum buffer size for parsed result */
 #define CMDLINE_PARSE_RESULT_BUFSIZE 8192
 
+/* maximum number of dynamic tokens */
+#define CMDLINE_PARSE_DYNAMIC_TOKENS 128
+
 /**
  * Stores a pointer to the ops struct, and the offset: the place to
  * write the parsed result in the destination structure.
@@ -130,6 +133,24 @@ struct cmdline;
  * Store a instruction, which is a pointer to a callback function and
  * its parameter that is called when the instruction is parsed, a help
  * string, and a list of token composing this instruction.
+ *
+ * When no tokens are defined (tokens[0] == NULL), they are retrieved
+ * dynamically by calling f() as follows:
+ *
+ *  f((struct cmdline_token_hdr **)&token_hdr,
+ *    NULL,
+ *    (struct cmdline_token_hdr *[])tokens));
+ *
+ * The address of the resulting token is expected at the location pointed by
+ * the first argument. Can be set to NULL to end the list.
+ *
+ * The cmdline argument (struct cmdline *) is always NULL.
+ *
+ * The last argument points to the NULL-terminated list of dynamic tokens
+ * defined so far. Since token_hdr points to an index of that list, the
+ * current index can be derived as follows:
+ *
+ *  int index = token_hdr - &(*tokens)[0];
  */
 struct cmdline_inst {
 	/* f(parsed_struct, data) */
Signature · 1 line
2.1.4
#126 lore raw

PATCH v2 5/25 cmdline: add alignment constraint

In reply to Adrien Mazarguil

This prevents sigbus errors on architectures that cannot handle unexpected unaligned accesses to the output buffer.

---
 lib/librte_cmdline/cmdline_parse.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
lib/librte_cmdline/cmdline_parse.c +6 -3
index 14f5553..763c286 100644
@@ -255,7 +255,10 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 	unsigned int inst_num=0;
 	cmdline_parse_inst_t *inst;
 	const char *curbuf;
-	char result_buf[CMDLINE_PARSE_RESULT_BUFSIZE];
+	union {
+		char buf[CMDLINE_PARSE_RESULT_BUFSIZE];
+		long double align; /* strong alignment constraint for buf */
+	} result;
 	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	void (*f)(void *, struct cmdline *, void *) = NULL;
 	void *data = NULL;
@@ -318,7 +321,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 		debug_printf("INST %d\n", inst_num);
 
 		/* fully parsed */
-		tok = match_inst(inst, buf, 0, result_buf, sizeof(result_buf),
+		tok = match_inst(inst, buf, 0, result.buf, sizeof(result.buf),
 				 &dyn_tokens);
 
 		if (tok > 0) /* we matched at least one token */
@@ -353,7 +356,7 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 
 	/* call func */
 	if (f) {
-		f(result_buf, cl, data);
+		f(result.buf, cl, data);
 	}
 
 	/* no match */
Signature · 1 line
2.1.4
#127 lore raw

PATCH v2 6/25 app/testpmd: implement basic support for rte_flow

In reply to Adrien Mazarguil

Add basic management functions for the generic flow API (validate, create, destroy, flush, query and list). Flow rule objects and properties are arranged in lists associated with each port.

---
 app/test-pmd/cmdline.c     |   1 +
 app/test-pmd/config.c      | 485 ++++++++++++++++++++++++++++++++++++++++
 app/test-pmd/csumonly.c    |   1 +
 app/test-pmd/flowgen.c     |   1 +
 app/test-pmd/icmpecho.c    |   1 +
 app/test-pmd/ieee1588fwd.c |   1 +
 app/test-pmd/iofwd.c       |   1 +
 app/test-pmd/macfwd.c      |   1 +
 app/test-pmd/macswap.c     |   1 +
 app/test-pmd/parameters.c  |   1 +
 app/test-pmd/rxonly.c      |   1 +
 app/test-pmd/testpmd.c     |   6 +
 app/test-pmd/testpmd.h     |  27 +++
 app/test-pmd/txonly.c      |   1 +
 14 files changed, 529 insertions(+)
app/test-pmd/cmdline.c +1 -0
index d03a592..5d1c0dd 100644
@@ -75,6 +75,7 @@
 #include <rte_string_fns.h>
 #include <rte_devargs.h>
 #include <rte_eth_ctrl.h>
+#include <rte_flow.h>
 
 #include <cmdline_rdline.h>
 #include <cmdline_parse.h>
app/test-pmd/config.c +485 -0

497 lines not on this page · raw message

app/test-pmd/csumonly.c +1 -0
index 57e6ae2..dd67ebf 100644
@@ -70,6 +70,7 @@
 #include <rte_sctp.h>
 #include <rte_prefetch.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 #include "testpmd.h"
 
 #define IP_DEFTTL  64   /* from RFC 1340. */
app/test-pmd/flowgen.c +1 -0
index b13ff89..13b4f90 100644
@@ -68,6 +68,7 @@
 #include <rte_tcp.h>
 #include <rte_udp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/icmpecho.c +1 -0
index 6a4e750..f25a8f5 100644
@@ -61,6 +61,7 @@
 #include <rte_ip.h>
 #include <rte_icmp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/ieee1588fwd.c +1 -0
index 0d3b37a..51170ee 100644
@@ -34,6 +34,7 @@
 
 #include <rte_cycles.h>
 #include <rte_ethdev.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/iofwd.c +1 -0
index 26936b7..15cb4a2 100644
@@ -64,6 +64,7 @@
 #include <rte_ether.h>
 #include <rte_ethdev.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/macfwd.c +1 -0
index 86e01de..d361db1 100644
@@ -65,6 +65,7 @@
 #include <rte_ethdev.h>
 #include <rte_ip.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/macswap.c +1 -0
index 36e139f..f996039 100644
@@ -65,6 +65,7 @@
 #include <rte_ethdev.h>
 #include <rte_ip.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/parameters.c +1 -0
index 08e5a76..28db8cd 100644
@@ -76,6 +76,7 @@
 #ifdef RTE_LIBRTE_PMD_BOND
 #include <rte_eth_bond.h>
 #endif
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/rxonly.c +1 -0
index fff815c..cf00576 100644
@@ -67,6 +67,7 @@
 #include <rte_ip.h>
 #include <rte_udp.h>
 #include <rte_net.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
app/test-pmd/testpmd.c +6 -0
index a0332c2..bfb2f8e 100644
@@ -78,6 +78,7 @@
 #ifdef RTE_LIBRTE_PDUMP
 #include <rte_pdump.h>
 #endif
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
@@ -1545,6 +1546,8 @@ close_port(portid_t pid)
 			continue;
 		}
 
+		if (port->flow_list)
+			port_flow_flush(pi);
 		rte_eth_dev_close(pi);
 
 		if (rte_atomic16_cmpset(&(port->port_status),
@@ -1599,6 +1602,9 @@ detach_port(uint8_t port_id)
 		return;
 	}
 
+	if (ports[port_id].flow_list)
+		port_flow_flush(port_id);
+
 	if (rte_eth_dev_detach(port_id, name))
 		return;
 
app/test-pmd/testpmd.h +27 -0
index 9c1e703..22ce2d6 100644
@@ -144,6 +144,19 @@ struct fwd_stream {
 /** Insert double VLAN header in forward engine */
 #define TESTPMD_TX_OFFLOAD_INSERT_QINQ       0x0080
 
+/** Descriptor for a single flow. */
+struct port_flow {
+	size_t size; /**< Allocated space including data[]. */
+	struct port_flow *next; /**< Next flow in list. */
+	struct port_flow *tmp; /**< Temporary linking. */
+	uint32_t id; /**< Flow rule ID. */
+	struct rte_flow *flow; /**< Opaque flow object returned by PMD. */
+	struct rte_flow_attr attr; /**< Attributes. */
+	struct rte_flow_item *pattern; /**< Pattern. */
+	struct rte_flow_action *actions; /**< Actions. */
+	uint8_t data[]; /**< Storage for pattern/actions. */
+};
+
 /**
  * The data structure associated with each port.
  */
@@ -177,6 +190,7 @@ struct rte_port {
 	struct ether_addr       *mc_addr_pool; /**< pool of multicast addrs */
 	uint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */
 	uint8_t                 slave_flag; /**< bonding slave port */
+	struct port_flow        *flow_list; /**< Associated flows. */
 };
 
 extern portid_t __rte_unused
@@ -504,6 +518,19 @@ void port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,
 			    uint8_t bit1_pos, uint8_t bit2_pos, uint32_t value);
 void port_reg_display(portid_t port_id, uint32_t reg_off);
 void port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t value);
+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);
+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);
+int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);
+int port_flow_flush(portid_t port_id);
+int port_flow_query(portid_t port_id, uint32_t rule,
+		    enum rte_flow_action_type action);
+void port_flow_list(portid_t port_id, uint32_t n, const uint32_t *group);
 
 void rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, uint16_t rxd_id);
 void tx_ring_desc_display(portid_t port_id, queueid_t txq_id, uint16_t txd_id);
app/test-pmd/txonly.c +1 -0
index 8513a06..e996f35 100644
@@ -68,6 +68,7 @@
 #include <rte_tcp.h>
 #include <rte_udp.h>
 #include <rte_string_fns.h>
+#include <rte_flow.h>
 
 #include "testpmd.h"
 
Signature · 1 line
2.1.4
#128 lore raw

Re: [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Hi Adrien,

Quoted 14 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Saturday, December 17, 2016 12:25 AM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Add basic management functions for the generic flow API (validate, create, destroy, flush, query and list). Flow rule objects and properties are arranged in lists associated with each port.

app/test-pmd/config.c +2 -0
+/** Create flow rule. */
+int

app/test-pmd/config.c port_mtu_set(portid_t port_id, uint16_t mtu) · line 1020

Quoted 10 lines
app/test-pmd/config.c +10 -0
+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) {
+	struct rte_flow *flow;
+	struct rte_port *port;
+	struct port_flow *pf;
+	uint32_t id;
+	struct rte_flow_error error;
+

I think there should be memset for error here, e.g. memset(&error, 0, sizeof(struct rte_flow_error));
Since both cause and message may be NULL regardless of the error type, if there's no error.cause and error.message returned from PMD, Segmentation fault will happen in port_flow_complain.
PS: This issue doesn't happen if add "export EXTRA_CFLAGS=' -g O0'" when compiling.

Thanks
Beilei

Quoted 30 lines
app/test-pmd/config.c +30 -0
+	flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+	if (!flow)
+		return port_flow_complain(&error);
+	port = &ports[port_id];
+	if (port->flow_list) {
+		if (port->flow_list->id == UINT32_MAX) {
+			printf("Highest rule ID is already assigned, delete"
+			       " it first");
+			rte_flow_destroy(port_id, flow, NULL);
+			return -ENOMEM;
+		}
+		id = port->flow_list->id + 1;
+	} else
+		id = 0;
+	pf = port_flow_new(attr, pattern, actions);
+	if (!pf) {
+		int err = rte_errno;
+
+		printf("Cannot allocate flow: %s\n", rte_strerror(err));
+		rte_flow_destroy(port_id, flow, NULL);
+		return -err;
+	}
+	pf->next = port->flow_list;
+	pf->id = id;
+	pf->flow = flow;
+	port->flow_list = pf;
+	printf("Flow rule #%u created\n", pf->id);
+	return 0;
+}
+
#129 lore raw

Re: [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Hi Beilei,

Quoted 22 lines · Beilei

Hi Adrien,

Quoted 20 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Saturday, December 17, 2016 12:25 AM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Add basic management functions for the generic flow API (validate, create, destroy, flush, query and list). Flow rule objects and properties are arranged in lists associated with each port.

app/test-pmd/config.c +8 -0
+/** Create flow rule. */
+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) {
+	struct rte_flow *flow;
+	struct rte_port *port;
Quoted 8 lines

app/test-pmd/config.c port_mtu_set(portid_t port_id, uint16_t mtu) · line 1020

Quoted 4 lines
app/test-pmd/config.c +4 -0
+	struct port_flow *pf;
+	uint32_t id;
+	struct rte_flow_error error;
+

I think there should be memset for error here, e.g. memset(&error, 0, sizeof(struct rte_flow_error));
Since both cause and message may be NULL regardless of the error type, if there's no error.cause and error.message returned from PMD, Segmentation fault will happen in port_flow_complain.
PS: This issue doesn't happen if add "export EXTRA_CFLAGS=' -g O0'" when compiling.

Actually, PMDs must fill the error structure only in case of error if the application provides one, it's not optional. I didn't initialize this structure for this reason.

I suggest we initialize it with a known poisoning value for debugging purposes though, to make it fail every time. Does it sound reasonable?

Quoted 30 lines
Quoted 30 lines
app/test-pmd/config.c +30 -0
+	flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+	if (!flow)
+		return port_flow_complain(&error);
+	port = &ports[port_id];
+	if (port->flow_list) {
+		if (port->flow_list->id == UINT32_MAX) {
+			printf("Highest rule ID is already assigned, delete"
+			       " it first");
+			rte_flow_destroy(port_id, flow, NULL);
+			return -ENOMEM;
+		}
+		id = port->flow_list->id + 1;
+	} else
+		id = 0;
+	pf = port_flow_new(attr, pattern, actions);
+	if (!pf) {
+		int err = rte_errno;
+
+		printf("Cannot allocate flow: %s\n", rte_strerror(err));
+		rte_flow_destroy(port_id, flow, NULL);
+		return -err;
+	}
+	pf->next = port->flow_list;
+	pf->id = id;
+	pf->flow = flow;
+	port->flow_list = pf;
+	printf("Flow rule #%u created\n", pf->id);
+	return 0;
+}
+
Signature · 2 lines
Adrien Mazarguil
6WIND
#130 lore raw

Re: [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Quoted 46 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Monday, December 19, 2016 6:20 PM
To: Xing, Beilei <[email protected]>
Cc: [email protected]; Pei, Yulong <[email protected]>
Subject: Re: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Hi Beilei,

Quoted 14 lines · Beilei

Hi Adrien,

Quoted 12 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Saturday, December 17, 2016 12:25 AM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Add basic management functions for the generic flow API (validate, create, destroy, flush, query and list). Flow rule objects and properties are arranged in lists associated with each port.

Quoted 16 lines

app/test-pmd/config.c port_mtu_set(portid_t port_id, uint16_t mtu) · line 1020

Quoted 13 lines
app/test-pmd/config.c +12 -0
+/** Create flow rule. */
+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) {
+	struct rte_flow *flow;
+	struct rte_port *port;
+	struct port_flow *pf;
+	uint32_t id;
+	struct rte_flow_error error;
+

I think there should be memset for error here, e.g. memset(&error, 0, sizeof(struct rte_flow_error)); Since both cause and message may be NULL

regardless of the error type, if there's no error.cause and error.message returned from PMD, Segmentation fault will happen in port_flow_complain.

Quoted 1 line

PS: This issue doesn't happen if add "export EXTRA_CFLAGS=' -g O0'" when

Quoted 8 lines

compiling.

Actually, PMDs must fill the error structure only in case of error if the application provides one, it's not optional. I didn't initialize this structure for this reason.

I suggest we initialize it with a known poisoning value for debugging purposes though, to make it fail every time. Does it sound reasonable?

OK, I see. Do you want PMD to allocate the memory for cause and message of error, and must fill the cause and message if error exists, right? So is it possible to set NULL for pointers of cause and message in application? then PMD can judge if it's need to allocate or overlap memory.

Quoted 35 lines
Quoted 23 lines
Quoted 23 lines
app/test-pmd/config.c +22 -0
+	flow = rte_flow_create(port_id, attr, pattern, actions, &error);
+	if (!flow)
+		return port_flow_complain(&error);
+	port = &ports[port_id];
+	if (port->flow_list) {
+		if (port->flow_list->id == UINT32_MAX) {
+			printf("Highest rule ID is already assigned, delete"
+			       " it first");
+			rte_flow_destroy(port_id, flow, NULL);
+			return -ENOMEM;
+		}
+		id = port->flow_list->id + 1;
+	} else
+		id = 0;
+	pf = port_flow_new(attr, pattern, actions);
+	if (!pf) {
+		int err = rte_errno;
+
+		printf("Cannot allocate flow: %s\n", rte_strerror(err));
+		rte_flow_destroy(port_id, flow, NULL);
+		return -err;
+	}
Quoted 8 lines

app/test-pmd/config.c port_mtu_set(portid_t port_id, uint16_t mtu) · line 1050

Quoted 8 lines
app/test-pmd/config.c +8 -0
+	pf->next = port->flow_list;
+	pf->id = id;
+	pf->flow = flow;
+	port->flow_list = pf;
+	printf("Flow rule #%u created\n", pf->id);
+	return 0;
+}
+

--
Adrien Mazarguil
6WIND

#131 lore raw

Re: [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Quoted 44 lines · Beilei
Quoted 44 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Monday, December 19, 2016 6:20 PM
To: Xing, Beilei <[email protected]>
Cc: [email protected]; Pei, Yulong <[email protected]>
Subject: Re: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Hi Beilei,

Quoted 14 lines · Beilei

Hi Adrien,

Quoted 12 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Saturday, December 17, 2016 12:25 AM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Add basic management functions for the generic flow API (validate, create, destroy, flush, query and list). Flow rule objects and properties are arranged in lists associated with each port.

Quoted 16 lines

app/test-pmd/config.c port_mtu_set(portid_t port_id, uint16_t mtu) · line 1020

Quoted 13 lines
app/test-pmd/config.c +12 -0
+/** Create flow rule. */
+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) {
+	struct rte_flow *flow;
+	struct rte_port *port;
+	struct port_flow *pf;
+	uint32_t id;
+	struct rte_flow_error error;
+

I think there should be memset for error here, e.g. memset(&error, 0, sizeof(struct rte_flow_error)); Since both cause and message may be NULL

regardless of the error type, if there's no error.cause and error.message returned from PMD, Segmentation fault will happen in port_flow_complain.

Quoted 1 line

PS: This issue doesn't happen if add "export EXTRA_CFLAGS=' -g O0'" when

Quoted 8 lines
Quoted 8 lines

compiling.

Actually, PMDs must fill the error structure only in case of error if the application provides one, it's not optional. I didn't initialize this structure for this reason.

I suggest we initialize it with a known poisoning value for debugging purposes though, to make it fail every time. Does it sound reasonable?

Done for v3 by the way.

Quoted 2 lines

OK, I see. Do you want PMD to allocate the memory for cause and message of error, and must fill the cause and message if error exists, right? So is it possible to set NULL for pointers of cause and message in application? then PMD can judge if it's need to allocate or overlap memory.

PMDs never allocate this structure, applications do and initialize it however they want. They only provide a non-NULL pointer if they want additional details in case of error.

It will likely be allocated on the stack in most cases (as in testpmd).

Quoted 1 line

From a PMD standpoint, the contents of this structure must be updated in

case of non-NULL pointer and error state.

Now the message pointer can be allocated dynamically but it's not recommended, it's far easier to make it point to some constant string. Applications won't free it anyway, so PMDs would have to do it during dev_close(). Please see "Verbose error reporting" documentation section.

Signature · 2 lines
Adrien Mazarguil
6WIND
#132 lore raw

Re: [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Quoted 81 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Tuesday, December 20, 2016 5:38 PM
To: Xing, Beilei <[email protected]>
Cc: [email protected]; Pei, Yulong <[email protected]>
Subject: Re: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Quoted 42 lines · Beilei
Quoted 42 lines

-----Original Message-----
From: Adrien Mazarguil [mailto:[email protected]]
Sent: Monday, December 19, 2016 6:20 PM
To: Xing, Beilei <[email protected]>
Cc: [email protected]; Pei, Yulong <[email protected]>
Subject: Re: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Hi Beilei,

Quoted 31 lines · Beilei

Hi Adrien,

Quoted 15 lines

-----Original Message-----
From: dev [mailto:[email protected]] On Behalf Of Adrien Mazarguil
Sent: Saturday, December 17, 2016 12:25 AM
To: [email protected]
Subject: [dpdk-dev] [PATCH v2 06/25] app/testpmd: implement basic support for rte_flow

Add basic management functions for the generic flow API (validate, create, destroy, flush, query and list). Flow rule objects and properties are arranged in lists associated with each port.

app/test-pmd/config.c +2 -0
+/** Create flow rule. */
+int

app/test-pmd/config.c port_mtu_set(portid_t port_id, uint16_t mtu) · line 1020

Quoted 10 lines
app/test-pmd/config.c +10 -0
+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) {
+	struct rte_flow *flow;
+	struct rte_port *port;
+	struct port_flow *pf;
+	uint32_t id;
+	struct rte_flow_error error;
+

I think there should be memset for error here, e.g. memset(&error, 0, sizeof(struct rte_flow_error)); Since both cause and message may be NULL

Quoted 2 lines
Quoted 2 lines

regardless of the error type, if there's no error.cause and error.message returned from PMD, Segmentation fault will happen in

port_flow_complain.

Quoted 10 lines
Quoted 10 lines
Quoted 2 lines

PS: This issue doesn't happen if add "export EXTRA_CFLAGS=' -g O0'" when

compiling.

Actually, PMDs must fill the error structure only in case of error if the application provides one, it's not optional. I didn't initialize this structure for this reason.

I suggest we initialize it with a known poisoning value for debugging purposes though, to make it fail every time. Does it sound

reasonable?

Done for v3 by the way.

Quoted 1 line

OK, I see. Do you want PMD to allocate the memory for cause and message

of error, and must fill the cause and message if error exists, right?

Quoted 1 line

So is it possible to set NULL for pointers of cause and message in application?

then PMD can judge if it's need to allocate or overlap memory.

PMDs never allocate this structure, applications do and initialize it however they want. They only provide a non-NULL pointer if they want additional details in case of error.

It will likely be allocated on the stack in most cases (as in testpmd).

Quoted 7 lines

From a PMD standpoint, the contents of this structure must be updated in case of non-NULL pointer and error state.

Now the message pointer can be allocated dynamically but it's not recommended, it's far easier to make it point to some constant string. Applications won't free it anyway, so PMDs would have to do it during dev_close(). Please see "Verbose error reporting" documentation section.

Got it, thanks. Seems the rte_flow_error_set function can be used for PMD.

Regards,
Beilei

#133 lore raw

PATCH v2 7/25 app/testpmd: add flow command

In reply to Adrien Mazarguil

Managing generic flow API functions from command line requires the use of dynamic tokens for convenience as flow rules are not fixed and cannot be defined statically.

This commit adds specific flexible parser code and object for a new "flow" command in separate file.

---
 app/test-pmd/Makefile       |   1 +
 app/test-pmd/cmdline.c      |   4 +
 app/test-pmd/cmdline_flow.c | 439 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 444 insertions(+)
app/test-pmd/Makefile +1 -0
index 891b85a..5988c3e 100644
@@ -47,6 +47,7 @@ CFLAGS += $(WERROR_FLAGS)
 SRCS-y := testpmd.c
 SRCS-y += parameters.c
 SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline.c
+SRCS-$(CONFIG_RTE_LIBRTE_CMDLINE) += cmdline_flow.c
 SRCS-y += config.c
 SRCS-y += iofwd.c
 SRCS-y += macfwd.c
app/test-pmd/cmdline.c +4 -0
index 5d1c0dd..b124412 100644
@@ -9567,6 +9567,9 @@ cmdline_parse_inst_t cmd_set_flow_director_flex_payload = {
 	},
 };
 
+/* Generic flow interface command. */
+extern cmdline_parse_inst_t cmd_flow;
+
 /* *** Classification Filters Control *** */
 /* *** Get symmetric hash enable per port *** */
 struct cmd_get_sym_hash_ena_per_port_result {
@@ -11605,6 +11608,7 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_set_hash_global_config,
 	(cmdline_parse_inst_t *)&cmd_set_hash_input_set,
 	(cmdline_parse_inst_t *)&cmd_set_fdir_input_set,
+	(cmdline_parse_inst_t *)&cmd_flow,
 	(cmdline_parse_inst_t *)&cmd_mcast_addr,
 	(cmdline_parse_inst_t *)&cmd_config_l2_tunnel_eth_type_all,
 	(cmdline_parse_inst_t *)&cmd_config_l2_tunnel_eth_type_specific,
app/test-pmd/cmdline_flow.c new file +439 -0

439 lines not on this page · raw message

Signature · 1 line
2.1.4
#134 lore raw

PATCH v2 8/25 app/testpmd: add rte_flow integer support

In reply to Adrien Mazarguil

Parse all integer types and handle conversion to network byte order in a single function.

---
 app/test-pmd/cmdline_flow.c | 148 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)
app/test-pmd/cmdline_flow.c +148 -0
index a36da06..81281f9 100644
@@ -34,11 +34,14 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
 #include <ctype.h>
 #include <string.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
+#include <rte_byteorder.h>
 #include <cmdline_parse.h>
 #include <rte_flow.h>
 
@@ -50,6 +53,10 @@ enum index {
 	ZERO = 0,
 	END,
 
+	/* Common tokens. */
+	INTEGER,
+	UNSIGNED,
+
 	/* Top-level command. */
 	FLOW,
 };
@@ -61,12 +68,24 @@ enum index {
 struct context {
 	/** Stack of subsequent token lists to process. */
 	const enum index *next[CTX_STACK_SIZE];
+	/** Arguments for stacked tokens. */
+	const void *args[CTX_STACK_SIZE];
 	enum index curr; /**< Current token index. */
 	enum index prev; /**< Index of the last token seen. */
 	int next_num; /**< Number of entries in next[]. */
+	int args_num; /**< Number of entries in args[]. */
 	uint32_t reparse:1; /**< Start over from the beginning. */
 	uint32_t eol:1; /**< EOL has been detected. */
 	uint32_t last:1; /**< No more arguments. */
+	void *object; /**< Address of current object for relative offsets. */
+};
+
+/** Token argument. */
+struct arg {
+	uint32_t hton:1; /**< Use network byte ordering. */
+	uint32_t sign:1; /**< Value is signed. */
+	uint32_t offset; /**< Relative offset from ctx->object. */
+	uint32_t size; /**< Field size. */
 };
 
 /** Parser token definition. */
@@ -80,6 +99,8 @@ struct token {
 	 * parser consumes the last entry of that stack.
 	 */
 	const enum index *const *next;
+	/** Arguments stack for subsequent tokens that need them. */
+	const struct arg *const *args;
 	/**
 	 * Token-processing callback, returns -1 in case of error, the
 	 * length of the matched string otherwise. If NULL, attempts to
@@ -112,6 +133,22 @@ struct token {
 /** Static initializer for a NEXT() entry. */
 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
 
+/** Static initializer for the args field. */
+#define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
+
+/** Static initializer for ARGS() to target a field. */
+#define ARGS_ENTRY(s, f) \
+	(&(const struct arg){ \
+		.offset = offsetof(s, f), \
+		.size = sizeof(((s *)0)->f), \
+	})
+
+/** Static initializer for ARGS() to target a pointer. */
+#define ARGS_ENTRY_PTR(s, f) \
+	(&(const struct arg){ \
+		.size = sizeof(*((s *)0)->f), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -121,6 +158,11 @@ struct buffer {
 static int parse_init(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);
+static int comp_none(struct context *, const struct token *,
+		     unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -135,6 +177,21 @@ static const struct token token_list[] = {
 		.type = "RETURN",
 		.help = "command may end here",
 	},
+	/* Common tokens. */
+	[INTEGER] = {
+		.name = "{int}",
+		.type = "INTEGER",
+		.help = "integer value",
+		.call = parse_int,
+		.comp = comp_none,
+	},
+	[UNSIGNED] = {
+		.name = "{unsigned}",
+		.type = "UNSIGNED",
+		.help = "unsigned integer value",
+		.call = parse_int,
+		.comp = comp_none,
+	},
 	/* Top-level command. */
 	[FLOW] = {
 		.name = "flow",
@@ -144,6 +201,23 @@ static const struct token token_list[] = {
 	},
 };
 
+/** Remove and return last entry from argument stack. */
+static const struct arg *
+pop_args(struct context *ctx)
+{
+	return ctx->args_num ? ctx->args[--ctx->args_num] : NULL;
+}
+
+/** Add entry on top of the argument stack. */
+static int
+push_args(struct context *ctx, const struct arg *arg)
+{
+	if (ctx->args_num == CTX_STACK_SIZE)
+		return -1;
+	ctx->args[ctx->args_num++] = arg;
+	return 0;
+}
+
 /** Default parsing function for token name matching. */
 static int
 parse_default(struct context *ctx, const struct token *token,
@@ -178,9 +252,74 @@ parse_init(struct context *ctx, const struct token *token,
 	/* Initialize buffer. */
 	memset(out, 0x00, sizeof(*out));
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
+	ctx->object = out;
 	return len;
 }
 
+/**
+ * Parse signed/unsigned integers 8 to 64-bit long.
+ *
+ * Last argument (ctx->args) is retrieved to determine integer type and
+ * storage location.
+ */
+static int
+parse_int(struct context *ctx, const struct token *token,
+	  const char *str, unsigned int len,
+	  void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	uintmax_t u;
+	char *end;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	errno = 0;
+	u = arg->sign ?
+		(uintmax_t)strtoimax(str, &end, 0) :
+		strtoumax(str, &end, 0);
+	if (errno || (size_t)(end - str) != len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	size = arg->size;
+	switch (size) {
+	case sizeof(uint8_t):
+		*(uint8_t *)buf = u;
+		break;
+	case sizeof(uint16_t):
+		*(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
+		break;
+	case sizeof(uint32_t):
+		*(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
+		break;
+	case sizeof(uint64_t):
+		*(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
+		break;
+	default:
+		goto error;
+	}
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
+/** No completion. */
+static int
+comp_none(struct context *ctx, const struct token *token,
+	  unsigned int ent, char *buf, unsigned int size)
+{
+	(void)ctx;
+	(void)token;
+	(void)ent;
+	(void)buf;
+	(void)size;
+	return 0;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -195,9 +334,11 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->curr = 0;
 	ctx->prev = 0;
 	ctx->next_num = 0;
+	ctx->args_num = 0;
 	ctx->reparse = 0;
 	ctx->eol = 0;
 	ctx->last = 0;
+	ctx->object = NULL;
 }
 
 /** Parse a token (cmdline API). */
@@ -270,6 +411,13 @@ cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
 				return -1;
 			ctx->next[ctx->next_num++] = token->next[i];
 		}
+	/* Push arguments if any. */
+	if (token->args)
+		for (i = 0; token->args[i]; ++i) {
+			if (ctx->args_num == RTE_DIM(ctx->args))
+				return -1;
+			ctx->args[ctx->args_num++] = token->args[i];
+		}
 	return len;
 }
 
Signature · 1 line
2.1.4
#135 lore raw

PATCH v2 9/25 app/testpmd: add flow list command

In reply to Adrien Mazarguil

Syntax:

flow list {port_id} [group {group_id}] [...]

List configured flow rules on a port. Output can optionally be limited to a given set of group identifiers.

---
 app/test-pmd/cmdline.c      |   4 ++
 app/test-pmd/cmdline_flow.c | 141 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 145 insertions(+)
app/test-pmd/cmdline.c +4 -0
index b124412..0dc6c63 100644
@@ -810,6 +810,10 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"sctp-src-port|sctp-dst-port|sctp-veri-tag|none)"
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
+
+			"flow list {port_id} [group {group_id}] [...]\n"
+			"    List existing flow rules sorted by priority,"
+			" filtered by group identifiers.\n\n"
 		);
 	}
 }
app/test-pmd/cmdline_flow.c +141 -0

215 lines not on this page · raw message

Signature · 1 line
2.1.4
#136 lore raw

PATCH v2 10/25 app/testpmd: add flow flush command

In reply to Adrien Mazarguil

Syntax:

flow flush {port_id}

Destroy all flow rules on a port.

---
 app/test-pmd/cmdline.c      |  3 +++
 app/test-pmd/cmdline_flow.c | 43 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +3 -0
index 0dc6c63..6e2b289 100644
@@ -811,6 +811,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow flush {port_id}\n"
+			"    Destroy all flow rules.\n\n"
+
 			"flow list {port_id} [group {group_id}] [...]\n"
 			"    List existing flow rules sorted by priority,"
 			" filtered by group identifiers.\n\n"
app/test-pmd/cmdline_flow.c +42 -1

76 lines not on this page · raw message

Signature · 1 line
2.1.4
#137 lore raw

PATCH v2 11/25 app/testpmd: add flow destroy command

In reply to Adrien Mazarguil

Syntax:

flow destroy {port_id} rule {rule_id} [...]

Destroy a given set of flow rules associated with a port.

---
 app/test-pmd/cmdline.c      |   3 ++
 app/test-pmd/cmdline_flow.c | 106 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 108 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +3 -0
index 6e2b289..80ddda2 100644
@@ -811,6 +811,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow destroy {port_id} rule {rule_id} [...]\n"
+			"    Destroy specific flow rules.\n\n"
+
 			"flow flush {port_id}\n"
 			"    Destroy all flow rules.\n\n"
 
app/test-pmd/cmdline_flow.c +105 -1

185 lines not on this page · raw message

Signature · 1 line
2.1.4
#138 lore raw

PATCH v2 12/25 app/testpmd: add flow validate/create commands

In reply to Adrien Mazarguil

Syntax:

flow (validate|create) {port_id}

    [group {group_id}] [priority {level}] [ingress] [egress]
    pattern {item} [/ {item} [...]] / end
    actions {action} [/ {action} [...]] / end

Either check the validity of a flow rule or create it. Any number of pattern items and actions can be provided in any order. Completion is available for convenience.

This commit only adds support for the most basic item and action types, namely:

- END: terminates pattern items and actions lists.
- VOID: item/action filler, no operation.
- INVERT: inverted pattern matching, process packets that do not match.
- PASSTHRU: action that leaves packets up for additional processing by subsequent flow rules.

---
 app/test-pmd/cmdline.c      |  14 ++
 app/test-pmd/cmdline_flow.c | 314 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 327 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +14 -0
index 80ddda2..23f4b48 100644
@@ -811,6 +811,20 @@ static void cmd_help_long_parsed(void *parsed_result,
 			" (select|add)\n"
 			"    Set the input set for FDir.\n\n"
 
+			"flow validate {port_id}"
+			" [group {group_id}] [priority {level}]"
+			" [ingress] [egress]"
+			" pattern {item} [/ {item} [...]] / end"
+			" actions {action} [/ {action} [...]] / end\n"
+			"    Check whether a flow rule can be created.\n\n"
+
+			"flow create {port_id}"
+			" [group {group_id}] [priority {level}]"
+			" [ingress] [egress]"
+			" pattern {item} [/ {item} [...]] / end"
+			" actions {action} [/ {action} [...]] / end\n"
+			"    Create a flow rule.\n\n"
+
 			"flow destroy {port_id} rule {rule_id} [...]\n"
 			"    Destroy specific flow rules.\n\n"
 
app/test-pmd/cmdline_flow.c +313 -1

445 lines not on this page · raw message

Signature · 1 line
2.1.4
#139 lore raw

PATCH v2 13/25 app/testpmd: add flow query command

In reply to Adrien Mazarguil

Syntax:

flow query {port_id} {rule_id} {action}

Query a specific action of an existing flow rule.

---
 app/test-pmd/cmdline.c      |   3 +
 app/test-pmd/cmdline_flow.c | 121 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 123 insertions(+), 1 deletion(-)
app/test-pmd/cmdline.c +3 -0
index 23f4b48..f768b6b 100644
@@ -831,6 +831,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"flow flush {port_id}\n"
 			"    Destroy all flow rules.\n\n"
 
+			"flow query {port_id} {rule_id} {action}\n"
+			"    Query an existing flow rule.\n\n"
+
 			"flow list {port_id} [group {group_id}] [...]\n"
 			"    List existing flow rules sorted by priority,"
 			" filtered by group identifiers.\n\n"
app/test-pmd/cmdline_flow.c +120 -1

186 lines not on this page · raw message

Signature · 1 line
2.1.4
#140 lore raw

PATCH v2 14/25 app/testpmd: add rte_flow item spec handler

In reply to Adrien Mazarguil

Add parser code to fully set individual fields of pattern item specification structures, using the following operators:

- fix: sets field and applies full bit-mask for perfect matching.
- spec: sets field without modifying its bit-mask.
- last: sets upper value of the spec => last range.
- mask: sets bit-mask affecting both spec and last from arbitrary value.

---
 app/test-pmd/cmdline_flow.c | 111 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)
app/test-pmd/cmdline_flow.c +111 -0
index 8f7ec1d..b66fecf 100644
@@ -89,6 +89,10 @@ enum index {
 
 	/* Validate/create pattern. */
 	PATTERN,
+	ITEM_PARAM_IS,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
 	ITEM_NEXT,
 	ITEM_END,
 	ITEM_VOID,
@@ -121,6 +125,7 @@ struct context {
 	uint16_t port; /**< Current port ID (for completions). */
 	uint32_t objdata; /**< Object-specific data. */
 	void *object; /**< Address of current object for relative offsets. */
+	void *objmask; /**< Object a full mask must be written to. */
 };
 
 /** Token argument. */
@@ -267,6 +272,15 @@ static const enum index next_list_attr[] = {
 	ZERO,
 };
 
+__rte_unused
+static const enum index item_param[] = {
+	ITEM_PARAM_IS,
+	ITEM_PARAM_SPEC,
+	ITEM_PARAM_LAST,
+	ITEM_PARAM_MASK,
+	ZERO,
+};
+
 static const enum index next_item[] = {
 	ITEM_END,
 	ITEM_VOID,
@@ -287,6 +301,8 @@ static int parse_init(struct context *, const struct token *,
 static int parse_vc(struct context *, const struct token *,
 		    const char *, unsigned int,
 		    void *, unsigned int);
+static int parse_vc_spec(struct context *, const struct token *,
+			 const char *, unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -492,6 +508,26 @@ static const struct token token_list[] = {
 		.next = NEXT(next_item),
 		.call = parse_vc,
 	},
+	[ITEM_PARAM_IS] = {
+		.name = "is",
+		.help = "match value perfectly (with full bit-mask)",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_SPEC] = {
+		.name = "spec",
+		.help = "match value according to configured bit-mask",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_LAST] = {
+		.name = "last",
+		.help = "specify upper bound to establish a range",
+		.call = parse_vc_spec,
+	},
+	[ITEM_PARAM_MASK] = {
+		.name = "mask",
+		.help = "specify bit-mask with relevant bits set to one",
+		.call = parse_vc_spec,
+	},
 	[ITEM_NEXT] = {
 		.name = "/",
 		.help = "specify next pattern item",
@@ -605,6 +641,7 @@ parse_init(struct context *ctx, const struct token *token,
 	memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
 	ctx->objdata = 0;
 	ctx->object = out;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -632,11 +669,13 @@ parse_vc(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.vc.data = (uint8_t *)out + size;
 		return len;
 	}
 	ctx->objdata = 0;
 	ctx->object = &out->args.vc.attr;
+	ctx->objmask = NULL;
 	switch (ctx->curr) {
 	case GROUP:
 	case PRIORITY:
@@ -652,6 +691,7 @@ parse_vc(struct context *ctx, const struct token *token,
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
 		ctx->object = out->args.vc.pattern;
+		ctx->objmask = NULL;
 		return len;
 	case ACTIONS:
 		out->args.vc.actions =
@@ -660,6 +700,7 @@ parse_vc(struct context *ctx, const struct token *token,
 						out->args.vc.pattern_n),
 					       sizeof(double));
 		ctx->object = out->args.vc.actions;
+		ctx->objmask = NULL;
 		return len;
 	default:
 		if (!token->priv)
@@ -682,6 +723,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.pattern_n;
 		ctx->object = item;
+		ctx->objmask = NULL;
 	} else {
 		const struct parse_action_priv *priv = token->priv;
 		struct rte_flow_action *action =
@@ -698,6 +740,7 @@ parse_vc(struct context *ctx, const struct token *token,
 		};
 		++out->args.vc.actions_n;
 		ctx->object = action;
+		ctx->objmask = NULL;
 	}
 	memset(data, 0, data_size);
 	out->args.vc.data = data;
@@ -705,6 +748,60 @@ parse_vc(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse pattern item parameter type. */
+static int
+parse_vc_spec(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_item *item;
+	uint32_t data_size;
+	int index;
+	int objmask = 0;
+
+	(void)size;
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	/* Parse parameter types. */
+	switch (ctx->curr) {
+	case ITEM_PARAM_IS:
+		index = 0;
+		objmask = 1;
+		break;
+	case ITEM_PARAM_SPEC:
+		index = 0;
+		break;
+	case ITEM_PARAM_LAST:
+		index = 1;
+		break;
+	case ITEM_PARAM_MASK:
+		index = 2;
+		break;
+	default:
+		return -1;
+	}
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (!out->args.vc.pattern_n)
+		return -1;
+	item = &out->args.vc.pattern[out->args.vc.pattern_n - 1];
+	data_size = ctx->objdata / 3; /* spec, last, mask */
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data + (data_size * index);
+	if (objmask) {
+		ctx->objmask = out->args.vc.data + (data_size * 2); /* mask */
+		item->mask = ctx->objmask;
+	} else
+		ctx->objmask = NULL;
+	/* Update relevant item pointer. */
+	*((const void **[]){ &item->spec, &item->last, &item->mask })[index] =
+		ctx->object;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -727,6 +824,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.destroy.rule =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -737,6 +835,7 @@ parse_destroy(struct context *ctx, const struct token *token,
 		return -1;
 	ctx->objdata = 0;
 	ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -762,6 +861,7 @@ parse_flush(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 	}
 	return len;
 }
@@ -788,6 +888,7 @@ parse_query(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 	}
 	return len;
 }
@@ -849,6 +950,7 @@ parse_list(struct context *ctx, const struct token *token,
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		out->args.list.group =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -859,6 +961,7 @@ parse_list(struct context *ctx, const struct token *token,
 		return -1;
 	ctx->objdata = 0;
 	ctx->object = out->args.list.group + out->args.list.group_n++;
+	ctx->objmask = NULL;
 	return len;
 }
 
@@ -891,6 +994,7 @@ parse_int(struct context *ctx, const struct token *token,
 		return len;
 	buf = (uint8_t *)ctx->object + arg->offset;
 	size = arg->size;
+objmask:
 	switch (size) {
 	case sizeof(uint8_t):
 		*(uint8_t *)buf = u;
@@ -907,6 +1011,11 @@ parse_int(struct context *ctx, const struct token *token,
 	default:
 		goto error;
 	}
+	if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) {
+		u = -1;
+		buf = (uint8_t *)ctx->objmask + arg->offset;
+		goto objmask;
+	}
 	return len;
 error:
 	push_args(ctx, arg);
@@ -927,6 +1036,7 @@ parse_port(struct context *ctx, const struct token *token,
 	else {
 		ctx->objdata = 0;
 		ctx->object = out;
+		ctx->objmask = NULL;
 		size = sizeof(*out);
 	}
 	ret = parse_int(ctx, token, str, len, out, size);
@@ -1033,6 +1143,7 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->port = 0;
 	ctx->objdata = 0;
 	ctx->object = NULL;
+	ctx->objmask = NULL;
 }
 
 /** Parse a token (cmdline API). */
Signature · 1 line
2.1.4
#141 lore raw

PATCH v2 15/25 app/testpmd: add rte_flow item spec prefix length

In reply to Adrien Mazarguil

Generating bit-masks from prefix lengths is often more convenient than providing them entirely (e.g. to define IPv4 and IPv6 subnets).

This commit adds the "prefix" operator that assigns generated bit-masks to any pattern item specification field.

---
 app/test-pmd/cmdline_flow.c | 80 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)
app/test-pmd/cmdline_flow.c +80 -0
index b66fecf..07f895e 100644
@@ -56,6 +56,7 @@ enum index {
 	/* Common tokens. */
 	INTEGER,
 	UNSIGNED,
+	PREFIX,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -93,6 +94,7 @@ enum index {
 	ITEM_PARAM_SPEC,
 	ITEM_PARAM_LAST,
 	ITEM_PARAM_MASK,
+	ITEM_PARAM_PREFIX,
 	ITEM_NEXT,
 	ITEM_END,
 	ITEM_VOID,
@@ -278,6 +280,7 @@ static const enum index item_param[] = {
 	ITEM_PARAM_SPEC,
 	ITEM_PARAM_LAST,
 	ITEM_PARAM_MASK,
+	ITEM_PARAM_PREFIX,
 	ZERO,
 };
 
@@ -321,6 +324,9 @@ static int parse_list(struct context *, const struct token *,
 static int parse_int(struct context *, const struct token *,
 		     const char *, unsigned int,
 		     void *, unsigned int);
+static int parse_prefix(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -361,6 +367,13 @@ static const struct token token_list[] = {
 		.call = parse_int,
 		.comp = comp_none,
 	},
+	[PREFIX] = {
+		.name = "{prefix}",
+		.type = "PREFIX",
+		.help = "prefix length for bit-mask",
+		.call = parse_prefix,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -528,6 +541,11 @@ static const struct token token_list[] = {
 		.help = "specify bit-mask with relevant bits set to one",
 		.call = parse_vc_spec,
 	},
+	[ITEM_PARAM_PREFIX] = {
+		.name = "prefix",
+		.help = "generate bit-mask from a prefix length",
+		.call = parse_vc_spec,
+	},
 	[ITEM_NEXT] = {
 		.name = "/",
 		.help = "specify next pattern item",
@@ -605,6 +623,62 @@ push_args(struct context *ctx, const struct arg *arg)
 	return 0;
 }
 
+/**
+ * Parse a prefix length and generate a bit-mask.
+ *
+ * Last argument (ctx->args) is retrieved to determine mask size, storage
+ * location and whether the result must use network byte ordering.
+ */
+static int
+parse_prefix(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	static const uint8_t conv[] = "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe\xff";
+	char *end;
+	uintmax_t u;
+	unsigned int bytes;
+	unsigned int extra;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	errno = 0;
+	u = strtoumax(str, &end, 0);
+	if (errno || (size_t)(end - str) != len)
+		goto error;
+	bytes = u / 8;
+	extra = u % 8;
+	size = arg->size;
+	if (bytes > size || bytes + !!extra > size)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+	if (!arg->hton) {
+		memset((uint8_t *)buf + size - bytes, 0xff, bytes);
+		memset(buf, 0x00, size - bytes);
+		if (extra)
+			((uint8_t *)buf)[size - bytes - 1] = conv[extra];
+	} else
+#endif
+	{
+		memset(buf, 0xff, bytes);
+		memset((uint8_t *)buf + bytes, 0x00, size - bytes);
+		if (extra)
+			((uint8_t *)buf)[bytes] = conv[extra];
+	}
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Default parsing function for token name matching. */
 static int
 parse_default(struct context *ctx, const struct token *token,
@@ -776,6 +850,12 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 	case ITEM_PARAM_LAST:
 		index = 1;
 		break;
+	case ITEM_PARAM_PREFIX:
+		/* Modify next token to expect a prefix. */
+		if (ctx->next_num < 2)
+			return -1;
+		ctx->next[ctx->next_num - 2] = NEXT_ENTRY(PREFIX);
+		/* Fall through. */
 	case ITEM_PARAM_MASK:
 		index = 2;
 		break;
Signature · 1 line
2.1.4
#142 lore raw

PATCH v2 16/25 app/testpmd: add rte_flow bit-field support

In reply to Adrien Mazarguil

Several rte_flow structures expose bit-fields that cannot be set in a generic fashion at byte level. Add bit-mask support to handle them.

---
 app/test-pmd/cmdline_flow.c | 59 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)
app/test-pmd/cmdline_flow.c +59 -0
index 07f895e..69887fc 100644
@@ -136,6 +136,7 @@ struct arg {
 	uint32_t sign:1; /**< Value is signed. */
 	uint32_t offset; /**< Relative offset from ctx->object. */
 	uint32_t size; /**< Field size. */
+	const uint8_t *mask; /**< Bit-mask to use instead of offset/size. */
 };
 
 /** Parser token definition. */
@@ -195,6 +196,13 @@ struct token {
 		.size = sizeof(((s *)0)->f), \
 	})
 
+/** Static initializer for ARGS() to target a bit-field. */
+#define ARGS_ENTRY_BF(s, f, b) \
+	(&(const struct arg){ \
+		.size = sizeof(s), \
+		.mask = (const void *)&(const s){ .f = (1 << (b)) - 1 }, \
+	})
+
 /** Static initializer for ARGS() to target a pointer. */
 #define ARGS_ENTRY_PTR(s, f) \
 	(&(const struct arg){ \
@@ -623,6 +631,34 @@ push_args(struct context *ctx, const struct arg *arg)
 	return 0;
 }
 
+/** Spread value into buffer according to bit-mask. */
+static size_t
+arg_entry_bf_fill(void *dst, uintmax_t val, const struct arg *arg)
+{
+	uint32_t i;
+	size_t len = 0;
+
+	/* Endian conversion is not supported on bit-fields. */
+	if (!arg->mask || arg->hton)
+		return 0;
+	for (i = 0; i != arg->size; ++i) {
+		unsigned int shift = 0;
+		uint8_t *buf = (uint8_t *)dst + i;
+
+		for (shift = 0; arg->mask[i] >> shift; ++shift) {
+			if (!(arg->mask[i] & (1 << shift)))
+				continue;
+			++len;
+			if (!dst)
+				continue;
+			*buf &= ~(1 << shift);
+			*buf |= (val & 1) << shift;
+			val >>= 1;
+		}
+	}
+	return len;
+}
+
 /**
  * Parse a prefix length and generate a bit-mask.
  *
@@ -649,6 +685,23 @@ parse_prefix(struct context *ctx, const struct token *token,
 	u = strtoumax(str, &end, 0);
 	if (errno || (size_t)(end - str) != len)
 		goto error;
+	if (arg->mask) {
+		uintmax_t v = 0;
+
+		extra = arg_entry_bf_fill(NULL, 0, arg);
+		if (u > extra)
+			goto error;
+		if (!ctx->object)
+			return len;
+		extra -= u;
+		while (u--)
+			(v <<= 1, v |= 1);
+		v <<= extra;
+		if (!arg_entry_bf_fill(ctx->object, v, arg) ||
+		    !arg_entry_bf_fill(ctx->objmask, -1, arg))
+			goto error;
+		return len;
+	}
 	bytes = u / 8;
 	extra = u % 8;
 	size = arg->size;
@@ -1072,6 +1125,12 @@ parse_int(struct context *ctx, const struct token *token,
 		goto error;
 	if (!ctx->object)
 		return len;
+	if (arg->mask) {
+		if (!arg_entry_bf_fill(ctx->object, u, arg) ||
+		    !arg_entry_bf_fill(ctx->objmask, -1, arg))
+			goto error;
+		return len;
+	}
 	buf = (uint8_t *)ctx->object + arg->offset;
 	size = arg->size;
 objmask:
Signature · 1 line
2.1.4
#143 lore raw

PATCH v2 17/25 app/testpmd: add item any to flow command

In reply to Adrien Mazarguil

This pattern item matches any protocol in place of the current layer and has two properties:

- min: minimum number of layers covered (0 or more).
- max: maximum number of layers covered (0 means infinity).

---
 app/test-pmd/cmdline_flow.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)
app/test-pmd/cmdline_flow.c +22 -1
index 69887fc..1736954 100644
@@ -99,6 +99,8 @@ enum index {
 	ITEM_END,
 	ITEM_VOID,
 	ITEM_INVERT,
+	ITEM_ANY,
+	ITEM_ANY_NUM,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -282,7 +284,6 @@ static const enum index next_list_attr[] = {
 	ZERO,
 };
 
-__rte_unused
 static const enum index item_param[] = {
 	ITEM_PARAM_IS,
 	ITEM_PARAM_SPEC,
@@ -296,6 +297,13 @@ static const enum index next_item[] = {
 	ITEM_END,
 	ITEM_VOID,
 	ITEM_INVERT,
+	ITEM_ANY,
+	ZERO,
+};
+
+static const enum index item_any[] = {
+	ITEM_ANY_NUM,
+	ITEM_NEXT,
 	ZERO,
 };
 
@@ -580,6 +588,19 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
 		.call = parse_vc,
 	},
+	[ITEM_ANY] = {
+		.name = "any",
+		.help = "match any protocol for the current layer",
+		.priv = PRIV_ITEM(ANY, sizeof(struct rte_flow_item_any)),
+		.next = NEXT(item_any),
+		.call = parse_vc,
+	},
+	[ITEM_ANY_NUM] = {
+		.name = "num",
+		.help = "number of layers covered",
+		.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, num)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
Signature · 1 line
2.1.4
#144 lore raw

PATCH v2 18/25 app/testpmd: add various items to flow command

In reply to Adrien Mazarguil

- PF: match packets addressed to the physical function.
- VF: match packets addressed to a virtual function ID.
- PORT: device-specific physical port index to use.

---
 app/test-pmd/cmdline_flow.c | 53 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
app/test-pmd/cmdline_flow.c +53 -0
index 1736954..ac93679 100644
@@ -101,6 +101,11 @@ enum index {
 	ITEM_INVERT,
 	ITEM_ANY,
 	ITEM_ANY_NUM,
+	ITEM_PF,
+	ITEM_VF,
+	ITEM_VF_ID,
+	ITEM_PORT,
+	ITEM_PORT_INDEX,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -298,6 +303,9 @@ static const enum index next_item[] = {
 	ITEM_VOID,
 	ITEM_INVERT,
 	ITEM_ANY,
+	ITEM_PF,
+	ITEM_VF,
+	ITEM_PORT,
 	ZERO,
 };
 
@@ -307,6 +315,18 @@ static const enum index item_any[] = {
 	ZERO,
 };
 
+static const enum index item_vf[] = {
+	ITEM_VF_ID,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_port[] = {
+	ITEM_PORT_INDEX,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -601,6 +621,39 @@ static const struct token token_list[] = {
 		.next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, num)),
 	},
+	[ITEM_PF] = {
+		.name = "pf",
+		.help = "match packets addressed to the physical function",
+		.priv = PRIV_ITEM(PF, 0),
+		.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
+		.call = parse_vc,
+	},
+	[ITEM_VF] = {
+		.name = "vf",
+		.help = "match packets addressed to a virtual function ID",
+		.priv = PRIV_ITEM(VF, sizeof(struct rte_flow_item_vf)),
+		.next = NEXT(item_vf),
+		.call = parse_vc,
+	},
+	[ITEM_VF_ID] = {
+		.name = "id",
+		.help = "destination VF ID",
+		.next = NEXT(item_vf, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_vf, id)),
+	},
+	[ITEM_PORT] = {
+		.name = "port",
+		.help = "device-specific physical port index to use",
+		.priv = PRIV_ITEM(PORT, sizeof(struct rte_flow_item_port)),
+		.next = NEXT(item_port),
+		.call = parse_vc,
+	},
+	[ITEM_PORT_INDEX] = {
+		.name = "index",
+		.help = "physical port index",
+		.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
Signature · 1 line
2.1.4
#145 lore raw

PATCH v2 19/25 app/testpmd: add item raw to flow command

In reply to Adrien Mazarguil

Matches arbitrary byte strings with properties:

- relative: look for pattern after the previous item.
- search: search pattern from offset (see also limit).
- offset: absolute or relative offset for pattern.
- limit: search area limit for start of pattern.
- length: pattern length.
- pattern: byte string to look for.

---
 app/test-pmd/cmdline_flow.c | 208 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 208 insertions(+)
app/test-pmd/cmdline_flow.c +208 -0
index ac93679..dafb07f 100644
@@ -57,6 +57,8 @@ enum index {
 	INTEGER,
 	UNSIGNED,
 	PREFIX,
+	BOOLEAN,
+	STRING,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -106,6 +108,12 @@ enum index {
 	ITEM_VF_ID,
 	ITEM_PORT,
 	ITEM_PORT_INDEX,
+	ITEM_RAW,
+	ITEM_RAW_RELATIVE,
+	ITEM_RAW_SEARCH,
+	ITEM_RAW_OFFSET,
+	ITEM_RAW_LIMIT,
+	ITEM_RAW_PATTERN,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -115,6 +123,13 @@ enum index {
 	ACTION_PASSTHRU,
 };
 
+/** Size of pattern[] field in struct rte_flow_item_raw. */
+#define ITEM_RAW_PATTERN_SIZE 36
+
+/** Storage size for struct rte_flow_item_raw including pattern. */
+#define ITEM_RAW_SIZE \
+	(offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -216,6 +231,13 @@ struct token {
 		.size = sizeof(*((s *)0)->f), \
 	})
 
+/** Static initializer for ARGS() with arbitrary size. */
+#define ARGS_ENTRY_USZ(s, f, sz) \
+	(&(const struct arg){ \
+		.offset = offsetof(s, f), \
+		.size = (sz), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -306,6 +328,7 @@ static const enum index next_item[] = {
 	ITEM_PF,
 	ITEM_VF,
 	ITEM_PORT,
+	ITEM_RAW,
 	ZERO,
 };
 
@@ -327,6 +350,16 @@ static const enum index item_port[] = {
 	ZERO,
 };
 
+static const enum index item_raw[] = {
+	ITEM_RAW_RELATIVE,
+	ITEM_RAW_SEARCH,
+	ITEM_RAW_OFFSET,
+	ITEM_RAW_LIMIT,
+	ITEM_RAW_PATTERN,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -363,11 +396,19 @@ static int parse_int(struct context *, const struct token *,
 static int parse_prefix(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
+static int parse_boolean(struct context *, const struct token *,
+			 const char *, unsigned int,
+			 void *, unsigned int);
+static int parse_string(struct context *, const struct token *,
+			const char *, unsigned int,
+			void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
+static int comp_boolean(struct context *, const struct token *,
+			unsigned int, char *, unsigned int);
 static int comp_action(struct context *, const struct token *,
 		       unsigned int, char *, unsigned int);
 static int comp_port(struct context *, const struct token *,
@@ -410,6 +451,20 @@ static const struct token token_list[] = {
 		.call = parse_prefix,
 		.comp = comp_none,
 	},
+	[BOOLEAN] = {
+		.name = "{boolean}",
+		.type = "BOOLEAN",
+		.help = "any boolean value",
+		.call = parse_boolean,
+		.comp = comp_boolean,
+	},
+	[STRING] = {
+		.name = "{string}",
+		.type = "STRING",
+		.help = "fixed string",
+		.call = parse_string,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -654,6 +709,52 @@ static const struct token token_list[] = {
 		.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)),
 	},
+	[ITEM_RAW] = {
+		.name = "raw",
+		.help = "match an arbitrary byte string",
+		.priv = PRIV_ITEM(RAW, ITEM_RAW_SIZE),
+		.next = NEXT(item_raw),
+		.call = parse_vc,
+	},
+	[ITEM_RAW_RELATIVE] = {
+		.name = "relative",
+		.help = "look for pattern after the previous item",
+		.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
+					   relative, 1)),
+	},
+	[ITEM_RAW_SEARCH] = {
+		.name = "search",
+		.help = "search pattern from offset (see also limit)",
+		.next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
+					   search, 1)),
+	},
+	[ITEM_RAW_OFFSET] = {
+		.name = "offset",
+		.help = "absolute or relative offset for pattern",
+		.next = NEXT(item_raw, NEXT_ENTRY(INTEGER), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, offset)),
+	},
+	[ITEM_RAW_LIMIT] = {
+		.name = "limit",
+		.help = "search area limit for start of pattern",
+		.next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, limit)),
+	},
+	[ITEM_RAW_PATTERN] = {
+		.name = "pattern",
+		.help = "byte string to look for",
+		.next = NEXT(item_raw,
+			     NEXT_ENTRY(STRING),
+			     NEXT_ENTRY(ITEM_PARAM_IS,
+					ITEM_PARAM_SPEC,
+					ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, length),
+			     ARGS_ENTRY_USZ(struct rte_flow_item_raw,
+					    pattern,
+					    ITEM_RAW_PATTERN_SIZE)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1235,6 +1336,96 @@ parse_int(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse a string.
+ *
+ * Two arguments (ctx->args) are retrieved from the stack to store data and
+ * its length (in that order).
+ */
+static int
+parse_string(struct context *ctx, const struct token *token,
+	     const char *str, unsigned int len,
+	     void *buf, unsigned int size)
+{
+	const struct arg *arg_data = pop_args(ctx);
+	const struct arg *arg_len = pop_args(ctx);
+	char tmp[16]; /* Ought to be enough. */
+	int ret;
+
+	/* Arguments are expected. */
+	if (!arg_data)
+		return -1;
+	if (!arg_len) {
+		push_args(ctx, arg_data);
+		return -1;
+	}
+	size = arg_data->size;
+	/* Bit-mask fill is not supported. */
+	if (arg_data->mask || size < len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	/* Let parse_int() fill length information first. */
+	ret = snprintf(tmp, sizeof(tmp), "%u", len);
+	if (ret < 0)
+		goto error;
+	push_args(ctx, arg_len);
+	ret = parse_int(ctx, token, tmp, ret, NULL, 0);
+	if (ret < 0) {
+		pop_args(ctx);
+		goto error;
+	}
+	buf = (uint8_t *)ctx->object + arg_data->offset;
+	/* Output buffer is not necessarily NUL-terminated. */
+	memcpy(buf, str, len);
+	memset((uint8_t *)buf + len, 0x55, size - len);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len);
+	return len;
+error:
+	push_args(ctx, arg_len);
+	push_args(ctx, arg_data);
+	return -1;
+}
+
+/** Boolean values (even indices stand for false). */
+static const char *const boolean_name[] = {
+	"0", "1",
+	"false", "true",
+	"no", "yes",
+	"N", "Y",
+	NULL,
+};
+
+/**
+ * Parse a boolean value.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_boolean(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	unsigned int i;
+	int ret;
+
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	for (i = 0; boolean_name[i]; ++i)
+		if (!strncmp(str, boolean_name[i], len))
+			break;
+	/* Process token as integer. */
+	if (boolean_name[i])
+		str = i & 1 ? "1" : "0";
+	push_args(ctx, arg);
+	ret = parse_int(ctx, token, str, strlen(str), buf, size);
+	return ret > 0 ? (int)len : ret;
+}
+
 /** Parse port and update context. */
 static int
 parse_port(struct context *ctx, const struct token *token,
@@ -1273,6 +1464,23 @@ comp_none(struct context *ctx, const struct token *token,
 	return 0;
 }
 
+/** Complete boolean values. */
+static int
+comp_boolean(struct context *ctx, const struct token *token,
+	     unsigned int ent, char *buf, unsigned int size)
+{
+	unsigned int i;
+
+	(void)ctx;
+	(void)token;
+	for (i = 0; boolean_name[i]; ++i)
+		if (buf && i == ent)
+			return snprintf(buf, size, "%s", boolean_name[i]);
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Complete action names. */
 static int
 comp_action(struct context *ctx, const struct token *token,
Signature · 1 line
2.1.4
#146 lore raw

PATCH v2 20/25 app/testpmd: add items eth/vlan to flow command

In reply to Adrien Mazarguil

These pattern items match basic Ethernet headers (source, destination and type) and related 802.1Q/ad VLAN headers.

---
 app/test-pmd/cmdline_flow.c | 126 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
app/test-pmd/cmdline_flow.c +126 -0
index dafb07f..53709fe 100644
@@ -43,6 +43,7 @@
 #include <rte_ethdev.h>
 #include <rte_byteorder.h>
 #include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
 #include <rte_flow.h>
 
 #include "testpmd.h"
@@ -59,6 +60,7 @@ enum index {
 	PREFIX,
 	BOOLEAN,
 	STRING,
+	MAC_ADDR,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -114,6 +116,13 @@ enum index {
 	ITEM_RAW_OFFSET,
 	ITEM_RAW_LIMIT,
 	ITEM_RAW_PATTERN,
+	ITEM_ETH,
+	ITEM_ETH_DST,
+	ITEM_ETH_SRC,
+	ITEM_ETH_TYPE,
+	ITEM_VLAN,
+	ITEM_VLAN_TPID,
+	ITEM_VLAN_TCI,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -238,6 +247,14 @@ struct token {
 		.size = (sz), \
 	})
 
+/** Same as ARGS_ENTRY() using network byte ordering. */
+#define ARGS_ENTRY_HTON(s, f) \
+	(&(const struct arg){ \
+		.hton = 1, \
+		.offset = offsetof(s, f), \
+		.size = sizeof(((s *)0)->f), \
+	})
+
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
 	enum index command; /**< Flow command. */
@@ -329,6 +346,8 @@ static const enum index next_item[] = {
 	ITEM_VF,
 	ITEM_PORT,
 	ITEM_RAW,
+	ITEM_ETH,
+	ITEM_VLAN,
 	ZERO,
 };
 
@@ -360,6 +379,21 @@ static const enum index item_raw[] = {
 	ZERO,
 };
 
+static const enum index item_eth[] = {
+	ITEM_ETH_DST,
+	ITEM_ETH_SRC,
+	ITEM_ETH_TYPE,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_vlan[] = {
+	ITEM_VLAN_TPID,
+	ITEM_VLAN_TCI,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -402,6 +436,9 @@ static int parse_boolean(struct context *, const struct token *,
 static int parse_string(struct context *, const struct token *,
 			const char *, unsigned int,
 			void *, unsigned int);
+static int parse_mac_addr(struct context *, const struct token *,
+			  const char *, unsigned int,
+			  void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -465,6 +502,13 @@ static const struct token token_list[] = {
 		.call = parse_string,
 		.comp = comp_none,
 	},
+	[MAC_ADDR] = {
+		.name = "{MAC address}",
+		.type = "MAC-48",
+		.help = "standard MAC address notation",
+		.call = parse_mac_addr,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -755,6 +799,50 @@ static const struct token token_list[] = {
 					    pattern,
 					    ITEM_RAW_PATTERN_SIZE)),
 	},
+	[ITEM_ETH] = {
+		.name = "eth",
+		.help = "match Ethernet header",
+		.priv = PRIV_ITEM(ETH, sizeof(struct rte_flow_item_eth)),
+		.next = NEXT(item_eth),
+		.call = parse_vc,
+	},
+	[ITEM_ETH_DST] = {
+		.name = "dst",
+		.help = "destination MAC",
+		.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, dst)),
+	},
+	[ITEM_ETH_SRC] = {
+		.name = "src",
+		.help = "source MAC",
+		.next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, src)),
+	},
+	[ITEM_ETH_TYPE] = {
+		.name = "type",
+		.help = "EtherType",
+		.next = NEXT(item_eth, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_eth, type)),
+	},
+	[ITEM_VLAN] = {
+		.name = "vlan",
+		.help = "match 802.1Q/ad VLAN tag",
+		.priv = PRIV_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)),
+		.next = NEXT(item_vlan),
+		.call = parse_vc,
+	},
+	[ITEM_VLAN_TPID] = {
+		.name = "tpid",
+		.help = "tag protocol identifier",
+		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tpid)),
+	},
+	[ITEM_VLAN_TCI] = {
+		.name = "tci",
+		.help = "tag control information",
+		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tci)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1388,6 +1476,44 @@ parse_string(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse a MAC address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_mac_addr(struct context *ctx, const struct token *token,
+	       const char *str, unsigned int len,
+	       void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	struct ether_addr tmp;
+	int ret;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	ret = cmdline_parse_etheraddr(NULL, str, &tmp, size);
+	if (ret < 0 || (unsigned int)ret != len)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Boolean values (even indices stand for false). */
 static const char *const boolean_name[] = {
 	"0", "1",
Signature · 1 line
2.1.4
#147 lore raw

PATCH v2 21/25 app/testpmd: add items ipv4/ipv6 to flow command

In reply to Adrien Mazarguil

Add the ability to match basic fields from IPv4 and IPv6 headers (source and destination addresses only).

---
 app/test-pmd/cmdline_flow.c | 177 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 177 insertions(+)
app/test-pmd/cmdline_flow.c +177 -0
index 53709fe..c2725a5 100644
@@ -38,6 +38,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include <string.h>
+#include <arpa/inet.h>
 
 #include <rte_common.h>
 #include <rte_ethdev.h>
@@ -61,6 +62,8 @@ enum index {
 	BOOLEAN,
 	STRING,
 	MAC_ADDR,
+	IPV4_ADDR,
+	IPV6_ADDR,
 	RULE_ID,
 	PORT_ID,
 	GROUP_ID,
@@ -123,6 +126,12 @@ enum index {
 	ITEM_VLAN,
 	ITEM_VLAN_TPID,
 	ITEM_VLAN_TCI,
+	ITEM_IPV4,
+	ITEM_IPV4_SRC,
+	ITEM_IPV4_DST,
+	ITEM_IPV6,
+	ITEM_IPV6_SRC,
+	ITEM_IPV6_DST,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -348,6 +357,8 @@ static const enum index next_item[] = {
 	ITEM_RAW,
 	ITEM_ETH,
 	ITEM_VLAN,
+	ITEM_IPV4,
+	ITEM_IPV6,
 	ZERO,
 };
 
@@ -394,6 +405,20 @@ static const enum index item_vlan[] = {
 	ZERO,
 };
 
+static const enum index item_ipv4[] = {
+	ITEM_IPV4_SRC,
+	ITEM_IPV4_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_ipv6[] = {
+	ITEM_IPV6_SRC,
+	ITEM_IPV6_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -439,6 +464,12 @@ static int parse_string(struct context *, const struct token *,
 static int parse_mac_addr(struct context *, const struct token *,
 			  const char *, unsigned int,
 			  void *, unsigned int);
+static int parse_ipv4_addr(struct context *, const struct token *,
+			   const char *, unsigned int,
+			   void *, unsigned int);
+static int parse_ipv6_addr(struct context *, const struct token *,
+			   const char *, unsigned int,
+			   void *, unsigned int);
 static int parse_port(struct context *, const struct token *,
 		      const char *, unsigned int,
 		      void *, unsigned int);
@@ -509,6 +540,20 @@ static const struct token token_list[] = {
 		.call = parse_mac_addr,
 		.comp = comp_none,
 	},
+	[IPV4_ADDR] = {
+		.name = "{IPv4 address}",
+		.type = "IPV4 ADDRESS",
+		.help = "standard IPv4 address notation",
+		.call = parse_ipv4_addr,
+		.comp = comp_none,
+	},
+	[IPV6_ADDR] = {
+		.name = "{IPv6 address}",
+		.type = "IPV6 ADDRESS",
+		.help = "standard IPv6 address notation",
+		.call = parse_ipv6_addr,
+		.comp = comp_none,
+	},
 	[RULE_ID] = {
 		.name = "{rule id}",
 		.type = "RULE ID",
@@ -843,6 +888,48 @@ static const struct token token_list[] = {
 		.next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param),
 		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tci)),
 	},
+	[ITEM_IPV4] = {
+		.name = "ipv4",
+		.help = "match IPv4 header",
+		.priv = PRIV_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)),
+		.next = NEXT(item_ipv4),
+		.call = parse_vc,
+	},
+	[ITEM_IPV4_SRC] = {
+		.name = "src",
+		.help = "source address",
+		.next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4,
+					     hdr.src_addr)),
+	},
+	[ITEM_IPV4_DST] = {
+		.name = "dst",
+		.help = "destination address",
+		.next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4,
+					     hdr.dst_addr)),
+	},
+	[ITEM_IPV6] = {
+		.name = "ipv6",
+		.help = "match IPv6 header",
+		.priv = PRIV_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)),
+		.next = NEXT(item_ipv6),
+		.call = parse_vc,
+	},
+	[ITEM_IPV6_SRC] = {
+		.name = "src",
+		.help = "source address",
+		.next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
+					     hdr.src_addr)),
+	},
+	[ITEM_IPV6_DST] = {
+		.name = "dst",
+		.help = "destination address",
+		.next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
+					     hdr.dst_addr)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1514,6 +1601,96 @@ parse_mac_addr(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+/**
+ * Parse an IPv4 address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_ipv4_addr(struct context *ctx, const struct token *token,
+		const char *str, unsigned int len,
+		void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	char str2[len + 1];
+	struct in_addr tmp;
+	int ret;
+
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	/* Only network endian is supported. */
+	if (!arg->hton)
+		goto error;
+	memcpy(str2, str, len);
+	str2[len] = '\0';
+	ret = inet_pton(AF_INET, str2, &tmp);
+	if (ret != 1) {
+		/* Attempt integer parsing. */
+		push_args(ctx, arg);
+		return parse_int(ctx, token, str, len, buf, size);
+	}
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
+/**
+ * Parse an IPv6 address.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_ipv6_addr(struct context *ctx, const struct token *token,
+		const char *str, unsigned int len,
+		void *buf, unsigned int size)
+{
+	const struct arg *arg = pop_args(ctx);
+	char str2[len + 1];
+	struct in6_addr tmp;
+	int ret;
+
+	(void)token;
+	/* Argument is expected. */
+	if (!arg)
+		return -1;
+	size = arg->size;
+	/* Bit-mask fill is not supported. */
+	if (arg->mask || size != sizeof(tmp))
+		goto error;
+	/* Only network endian is supported. */
+	if (!arg->hton)
+		goto error;
+	memcpy(str2, str, len);
+	str2[len] = '\0';
+	ret = inet_pton(AF_INET6, str2, &tmp);
+	if (ret != 1)
+		goto error;
+	if (!ctx->object)
+		return len;
+	buf = (uint8_t *)ctx->object + arg->offset;
+	memcpy(buf, &tmp, size);
+	if (ctx->objmask)
+		memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+	return len;
+error:
+	push_args(ctx, arg);
+	return -1;
+}
+
 /** Boolean values (even indices stand for false). */
 static const char *const boolean_name[] = {
 	"0", "1",
Signature · 1 line
2.1.4
#148 lore raw

PATCH v2 22/25 app/testpmd: add L4 items to flow command

In reply to Adrien Mazarguil

Add the ability to match a few properties of common L4[.5] protocol headers:

- ICMP: type and code.
- UDP: source and destination ports.
- TCP: source and destination ports.
- SCTP: source and destination ports.
- VXLAN: network identifier.

---
 app/test-pmd/cmdline_flow.c | 163 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+)
app/test-pmd/cmdline_flow.c +163 -0
index c2725a5..a340a75 100644
@@ -132,6 +132,20 @@ enum index {
 	ITEM_IPV6,
 	ITEM_IPV6_SRC,
 	ITEM_IPV6_DST,
+	ITEM_ICMP,
+	ITEM_ICMP_TYPE,
+	ITEM_ICMP_CODE,
+	ITEM_UDP,
+	ITEM_UDP_SRC,
+	ITEM_UDP_DST,
+	ITEM_TCP,
+	ITEM_TCP_SRC,
+	ITEM_TCP_DST,
+	ITEM_SCTP,
+	ITEM_SCTP_SRC,
+	ITEM_SCTP_DST,
+	ITEM_VXLAN,
+	ITEM_VXLAN_VNI,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -359,6 +373,11 @@ static const enum index next_item[] = {
 	ITEM_VLAN,
 	ITEM_IPV4,
 	ITEM_IPV6,
+	ITEM_ICMP,
+	ITEM_UDP,
+	ITEM_TCP,
+	ITEM_SCTP,
+	ITEM_VXLAN,
 	ZERO,
 };
 
@@ -419,6 +438,40 @@ static const enum index item_ipv6[] = {
 	ZERO,
 };
 
+static const enum index item_icmp[] = {
+	ITEM_ICMP_TYPE,
+	ITEM_ICMP_CODE,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_udp[] = {
+	ITEM_UDP_SRC,
+	ITEM_UDP_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_tcp[] = {
+	ITEM_TCP_SRC,
+	ITEM_TCP_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_sctp[] = {
+	ITEM_SCTP_SRC,
+	ITEM_SCTP_DST,
+	ITEM_NEXT,
+	ZERO,
+};
+
+static const enum index item_vxlan[] = {
+	ITEM_VXLAN_VNI,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -930,6 +983,103 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6,
 					     hdr.dst_addr)),
 	},
+	[ITEM_ICMP] = {
+		.name = "icmp",
+		.help = "match ICMP header",
+		.priv = PRIV_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)),
+		.next = NEXT(item_icmp),
+		.call = parse_vc,
+	},
+	[ITEM_ICMP_TYPE] = {
+		.name = "type",
+		.help = "ICMP packet type",
+		.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp,
+					     hdr.icmp_type)),
+	},
+	[ITEM_ICMP_CODE] = {
+		.name = "code",
+		.help = "ICMP packet code",
+		.next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp,
+					     hdr.icmp_code)),
+	},
+	[ITEM_UDP] = {
+		.name = "udp",
+		.help = "match UDP header",
+		.priv = PRIV_ITEM(UDP, sizeof(struct rte_flow_item_udp)),
+		.next = NEXT(item_udp),
+		.call = parse_vc,
+	},
+	[ITEM_UDP_SRC] = {
+		.name = "src",
+		.help = "UDP source port",
+		.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp,
+					     hdr.src_port)),
+	},
+	[ITEM_UDP_DST] = {
+		.name = "dst",
+		.help = "UDP destination port",
+		.next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp,
+					     hdr.dst_port)),
+	},
+	[ITEM_TCP] = {
+		.name = "tcp",
+		.help = "match TCP header",
+		.priv = PRIV_ITEM(TCP, sizeof(struct rte_flow_item_tcp)),
+		.next = NEXT(item_tcp),
+		.call = parse_vc,
+	},
+	[ITEM_TCP_SRC] = {
+		.name = "src",
+		.help = "TCP source port",
+		.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp,
+					     hdr.src_port)),
+	},
+	[ITEM_TCP_DST] = {
+		.name = "dst",
+		.help = "TCP destination port",
+		.next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp,
+					     hdr.dst_port)),
+	},
+	[ITEM_SCTP] = {
+		.name = "sctp",
+		.help = "match SCTP header",
+		.priv = PRIV_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)),
+		.next = NEXT(item_sctp),
+		.call = parse_vc,
+	},
+	[ITEM_SCTP_SRC] = {
+		.name = "src",
+		.help = "SCTP source port",
+		.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp,
+					     hdr.src_port)),
+	},
+	[ITEM_SCTP_DST] = {
+		.name = "dst",
+		.help = "SCTP destination port",
+		.next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp,
+					     hdr.dst_port)),
+	},
+	[ITEM_VXLAN] = {
+		.name = "vxlan",
+		.help = "match VXLAN header",
+		.priv = PRIV_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)),
+		.next = NEXT(item_vxlan),
+		.call = parse_vc,
+	},
+	[ITEM_VXLAN_VNI] = {
+		.name = "vni",
+		.help = "VXLAN identifier",
+		.next = NEXT(item_vxlan, NEXT_ENTRY(UNSIGNED), item_param),
+		.args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vxlan, vni)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -1491,6 +1641,19 @@ parse_int(struct context *ctx, const struct token *token,
 	case sizeof(uint16_t):
 		*(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
 		break;
+	case sizeof(uint8_t [3]):
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+		if (!arg->hton) {
+			((uint8_t *)buf)[0] = u;
+			((uint8_t *)buf)[1] = u >> 8;
+			((uint8_t *)buf)[2] = u >> 16;
+			break;
+		}
+#endif
+		((uint8_t *)buf)[0] = u >> 16;
+		((uint8_t *)buf)[1] = u >> 8;
+		((uint8_t *)buf)[2] = u;
+		break;
 	case sizeof(uint32_t):
 		*(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
 		break;
Signature · 1 line
2.1.4
#149 lore raw

PATCH v2 23/25 app/testpmd: add various actions to flow command

In reply to Adrien Mazarguil

- MARK: attach 32 bit value to packets.
- FLAG: flag packets.
- DROP: drop packets.
- COUNT: enable counters for a rule.
- PF: redirect packets to physical device function.
- VF: redirect packets to virtual device function.

---
 app/test-pmd/cmdline_flow.c | 121 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)
app/test-pmd/cmdline_flow.c +121 -0
index a340a75..90712bf 100644
@@ -153,6 +153,15 @@ enum index {
 	ACTION_END,
 	ACTION_VOID,
 	ACTION_PASSTHRU,
+	ACTION_MARK,
+	ACTION_MARK_ID,
+	ACTION_FLAG,
+	ACTION_DROP,
+	ACTION_COUNT,
+	ACTION_PF,
+	ACTION_VF,
+	ACTION_VF_ORIGINAL,
+	ACTION_VF_ID,
 };
 
 /** Size of pattern[] field in struct rte_flow_item_raw. */
@@ -476,6 +485,25 @@ static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
 	ACTION_PASSTHRU,
+	ACTION_MARK,
+	ACTION_FLAG,
+	ACTION_DROP,
+	ACTION_COUNT,
+	ACTION_PF,
+	ACTION_VF,
+	ZERO,
+};
+
+static const enum index action_mark[] = {
+	ACTION_MARK_ID,
+	ACTION_NEXT,
+	ZERO,
+};
+
+static const enum index action_vf[] = {
+	ACTION_VF_ORIGINAL,
+	ACTION_VF_ID,
+	ACTION_NEXT,
 	ZERO,
 };
 
@@ -487,6 +515,8 @@ static int parse_vc(struct context *, const struct token *,
 		    void *, unsigned int);
 static int parse_vc_spec(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
+static int parse_vc_conf(struct context *, const struct token *,
+			 const char *, unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -1112,6 +1142,70 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_MARK] = {
+		.name = "mark",
+		.help = "attach 32 bit value to packets",
+		.priv = PRIV_ACTION(MARK, sizeof(struct rte_flow_action_mark)),
+		.next = NEXT(action_mark),
+		.call = parse_vc,
+	},
+	[ACTION_MARK_ID] = {
+		.name = "id",
+		.help = "32 bit value to return with packets",
+		.next = NEXT(action_mark, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_mark, id)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_FLAG] = {
+		.name = "flag",
+		.help = "flag packets",
+		.priv = PRIV_ACTION(FLAG, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_DROP] = {
+		.name = "drop",
+		.help = "drop packets (note: passthru has priority)",
+		.priv = PRIV_ACTION(DROP, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_COUNT] = {
+		.name = "count",
+		.help = "enable counters for this rule",
+		.priv = PRIV_ACTION(COUNT, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_PF] = {
+		.name = "pf",
+		.help = "redirect packets to physical device function",
+		.priv = PRIV_ACTION(PF, 0),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc,
+	},
+	[ACTION_VF] = {
+		.name = "vf",
+		.help = "redirect packets to virtual device function",
+		.priv = PRIV_ACTION(VF, sizeof(struct rte_flow_action_vf)),
+		.next = NEXT(action_vf),
+		.call = parse_vc,
+	},
+	[ACTION_VF_ORIGINAL] = {
+		.name = "original",
+		.help = "use original VF ID if possible",
+		.next = NEXT(action_vf, NEXT_ENTRY(BOOLEAN)),
+		.args = ARGS(ARGS_ENTRY_BF(struct rte_flow_action_vf,
+					   original, 1)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_VF_ID] = {
+		.name = "id",
+		.help = "VF ID to redirect packets to",
+		.next = NEXT(action_vf, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_vf, id)),
+		.call = parse_vc_conf,
+	},
 };
 
 /** Remove and return last entry from argument stack. */
@@ -1435,6 +1529,33 @@ parse_vc_spec(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse action configuration field. */
+static int
+parse_vc_conf(struct context *ctx, const struct token *token,
+	      const char *str, unsigned int len,
+	      void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_action *action;
+
+	(void)size;
+	/* 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->args.vc.actions_n)
+		return -1;
+	action = &out->args.vc.actions[out->args.vc.actions_n - 1];
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data;
+	ctx->objmask = NULL;
+	/* Update configuration pointer. */
+	action->conf = ctx->object;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
Signature · 1 line
2.1.4
#150 lore raw

PATCH v2 24/25 app/testpmd: add queue actions to flow command

In reply to Adrien Mazarguil

- QUEUE: assign packets to a given queue index.
- DUP: duplicate packets to a given queue index.
- RSS: spread packets among several queues.

---
 app/test-pmd/cmdline_flow.c | 152 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 152 insertions(+)
app/test-pmd/cmdline_flow.c +152 -0
index 90712bf..2376b8f 100644
@@ -156,8 +156,15 @@ enum index {
 	ACTION_MARK,
 	ACTION_MARK_ID,
 	ACTION_FLAG,
+	ACTION_QUEUE,
+	ACTION_QUEUE_INDEX,
 	ACTION_DROP,
 	ACTION_COUNT,
+	ACTION_DUP,
+	ACTION_DUP_INDEX,
+	ACTION_RSS,
+	ACTION_RSS_QUEUES,
+	ACTION_RSS_QUEUE,
 	ACTION_PF,
 	ACTION_VF,
 	ACTION_VF_ORIGINAL,
@@ -171,6 +178,14 @@ enum index {
 #define ITEM_RAW_SIZE \
 	(offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
 
+/** Number of queue[] entries in struct rte_flow_action_rss. */
+#define ACTION_RSS_NUM 32
+
+/** Storage size for struct rte_flow_action_rss including queues. */
+#define ACTION_RSS_SIZE \
+	(offsetof(struct rte_flow_action_rss, queue) + \
+	 sizeof(*((struct rte_flow_action_rss *)0)->queue) * ACTION_RSS_NUM)
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -487,8 +502,11 @@ static const enum index next_action[] = {
 	ACTION_PASSTHRU,
 	ACTION_MARK,
 	ACTION_FLAG,
+	ACTION_QUEUE,
 	ACTION_DROP,
 	ACTION_COUNT,
+	ACTION_DUP,
+	ACTION_RSS,
 	ACTION_PF,
 	ACTION_VF,
 	ZERO,
@@ -500,6 +518,24 @@ static const enum index action_mark[] = {
 	ZERO,
 };
 
+static const enum index action_queue[] = {
+	ACTION_QUEUE_INDEX,
+	ACTION_NEXT,
+	ZERO,
+};
+
+static const enum index action_dup[] = {
+	ACTION_DUP_INDEX,
+	ACTION_NEXT,
+	ZERO,
+};
+
+static const enum index action_rss[] = {
+	ACTION_RSS_QUEUES,
+	ACTION_NEXT,
+	ZERO,
+};
+
 static const enum index action_vf[] = {
 	ACTION_VF_ORIGINAL,
 	ACTION_VF_ID,
@@ -517,6 +553,9 @@ static int parse_vc_spec(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
 static int parse_vc_conf(struct context *, const struct token *,
 			 const char *, unsigned int, void *, unsigned int);
+static int parse_vc_action_rss_queue(struct context *, const struct token *,
+				     const char *, unsigned int, void *,
+				     unsigned int);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -566,6 +605,8 @@ static int comp_port(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_rule_id(struct context *, const struct token *,
 			unsigned int, char *, unsigned int);
+static int comp_vc_action_rss_queue(struct context *, const struct token *,
+				    unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -1163,6 +1204,21 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_QUEUE] = {
+		.name = "queue",
+		.help = "assign packets to a given queue index",
+		.priv = PRIV_ACTION(QUEUE,
+				    sizeof(struct rte_flow_action_queue)),
+		.next = NEXT(action_queue),
+		.call = parse_vc,
+	},
+	[ACTION_QUEUE_INDEX] = {
+		.name = "index",
+		.help = "queue index to use",
+		.next = NEXT(action_queue, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_queue, index)),
+		.call = parse_vc_conf,
+	},
 	[ACTION_DROP] = {
 		.name = "drop",
 		.help = "drop packets (note: passthru has priority)",
@@ -1177,6 +1233,39 @@ static const struct token token_list[] = {
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
+	[ACTION_DUP] = {
+		.name = "dup",
+		.help = "duplicate packets to a given queue index",
+		.priv = PRIV_ACTION(DUP, sizeof(struct rte_flow_action_dup)),
+		.next = NEXT(action_dup),
+		.call = parse_vc,
+	},
+	[ACTION_DUP_INDEX] = {
+		.name = "index",
+		.help = "queue index to duplicate packets to",
+		.next = NEXT(action_dup, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_dup, index)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_RSS] = {
+		.name = "rss",
+		.help = "spread packets among several queues",
+		.priv = PRIV_ACTION(RSS, ACTION_RSS_SIZE),
+		.next = NEXT(action_rss),
+		.call = parse_vc,
+	},
+	[ACTION_RSS_QUEUES] = {
+		.name = "queues",
+		.help = "queue indices to use",
+		.next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_QUEUE)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_RSS_QUEUE] = {
+		.name = "{queue}",
+		.help = "queue index",
+		.call = parse_vc_action_rss_queue,
+		.comp = comp_vc_action_rss_queue,
+	},
 	[ACTION_PF] = {
 		.name = "pf",
 		.help = "redirect packets to physical device function",
@@ -1556,6 +1645,51 @@ parse_vc_conf(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/**
+ * Parse queue field for RSS action.
+ *
+ * Valid tokens are queue indices and the "end" token.
+ */
+static int
+parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
+			  const char *str, unsigned int len,
+			  void *buf, unsigned int size)
+{
+	static const enum index next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
+	int ret;
+	int i;
+
+	(void)token;
+	(void)buf;
+	(void)size;
+	if (ctx->curr != ACTION_RSS_QUEUE)
+		return -1;
+	i = ctx->objdata >> 16;
+	if (!strncmp(str, "end", len)) {
+		ctx->objdata &= 0xffff;
+		return len;
+	}
+	if (i >= ACTION_RSS_NUM)
+		return -1;
+	if (push_args(ctx, ARGS_ENTRY(struct rte_flow_action_rss, queue[i])))
+		return -1;
+	ret = parse_int(ctx, token, str, len, NULL, 0);
+	if (ret < 0) {
+		pop_args(ctx);
+		return -1;
+	}
+	++i;
+	ctx->objdata = i << 16 | (ctx->objdata & 0xffff);
+	/* Repeat token. */
+	if (ctx->next_num == RTE_DIM(ctx->next))
+		return -1;
+	ctx->next[ctx->next_num++] = next;
+	if (!ctx->object)
+		return len;
+	((struct rte_flow_action_rss *)ctx->object)->num = i;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -2130,6 +2264,24 @@ comp_rule_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+/** Complete queue field for RSS action. */
+static int
+comp_vc_action_rss_queue(struct context *ctx, const struct token *token,
+			 unsigned int ent, char *buf, unsigned int size)
+{
+	static const char *const str[] = { "", "end", NULL };
+	unsigned int i;
+
+	(void)ctx;
+	(void)token;
+	for (i = 0; str[i] != NULL; ++i)
+		if (buf && i == ent)
+			return snprintf(buf, size, "%s", str[i]);
+	if (buf)
+		return -1;
+	return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
Signature · 1 line
2.1.4