Skip to contentSkip to conversation

Building a better rte_flow parser

PATCH Superseded Anatoly Burakov 6 participants 243 messages Aug 20 – Oct 20 Page 1 of 5

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover Building a better rte_flow parser

Most rte_flow parsers in DPDK suffer from huge implementation complexity because even though 99% of what people use rte_flow parsers for is parsing protocol graphs, no parser is written explicitly as a graph. This patchset attempts to suggest a viable model to build rte_flow parsers as graphs, by offering a lightweight header only library to build rte_flow parsering graphs without too much boilerplate and complexity.

Most of the patchset is about Intel drivers, but they are meant as reimplementations as well as examples for the rest of the community to assess how to build parsers using this new infrastructure. I expect the first two patches will be of most interest to non-Intel reviewers, as they deal with building two reusable parser architecture pieces.

The first piece is a new flow graph helper in ethdev. Its purpose is deliberately narrow: it targets the protocol-graph part of rte_flow pattern parsing, where drivers walk packet headers and validate legal item sequences and parameters. That does not cover all possible rte_flow features, especially more exotic flow items, but it does cover a large and widely shared part of what existing drivers need to do. Or, to put it in other words, the only flow items this infrastructure *doesn't* cover is things that do not lend themselves well to be parsed as a graph of protocol headers (e.g. conntrack items). Everything else should be covered or cover-able. In practice, just about all drivers will benefit from graph parsing as all but one of them implement only the protocol stack parts, which are the ones targeted by the graph helper.

The second piece is a reusable flow engine framework for Intel Ethernet drivers. This is kept Intel-local because I do not feel it is even appropriate to define such a framework for all drivers to use in the first place. Even so, the intent is to establish a cleaner parser architecture with a defined interaction model, explicit memory ownership rules, and engine definitions that do not block secondary-process-safe usage. It is my hope that this would serve as a model for other drivers to follow, expand on, rework, and improve, so that maybe down the line we *might* have a common rte_flow infrastructure for drivers to use.

Most of the rest of the series is parser reimplementation, but that is mainly the vehicle for demonstrating and validating those two pieces. ixgbe and i40e are wired into the new common parsing path, and their existing parsers are migrated incrementally to the graph-based model. Besides reducing ad hoc parser code, this also makes validation more explicit and more consistent with the actual install path. In a few places that means invalid inputs that were previously ignored, deferred, or interpreted loosely are now rejected earlier and more strictly, without any increase in code complexity (in fact, with marked *decrease* of it!).

For now, there is a hack in

RFC -> v1:
- Fixed a lot of bugs, concurrency issues, and init/uninit sequence problems in Intel common flow engine implementation
- Fixed a few copypaste errors in various engines
- Improved comment documentation and flow engine docs

Anatoly Burakov (21):
ethdev: add flow graph API net/intel/common: add flow engines infrastructure net/intel/common: add utility functions net/ixgbe: add support for common flow parsing net/ixgbe: reimplement ethertype parser net/ixgbe: reimplement syn parser net/ixgbe: reimplement L2 tunnel parser net/ixgbe: reimplement ntuple parser net/ixgbe: reimplement security parser net/ixgbe: reimplement FDIR parser net/ixgbe: reimplement hash parser net/i40e: add support for common flow parsing net/i40e: reimplement ethertype parser net/i40e: reimplement FDIR parser net/i40e: reimplement tunnel QinQ parser net/i40e: reimplement VXLAN parser net/i40e: reimplement NVGRE parser net/i40e: reimplement MPLS parser net/i40e: reimplement gtp parser net/i40e: reimplement L4 cloud parser net/i40e: reimplement hash parser

 doc/guides/prog_guide/ethdev/flow_graph.rst   |  748 +++
 doc/guides/prog_guide/ethdev/index.rst        |    1 +
 doc/guides/rel_notes/release_26_11.rst        |    7 +
 drivers/net/intel/common/flow_engine.h        | 1373 ++++++
 drivers/net/intel/common/flow_util.h          |  183 +
 drivers/net/intel/i40e/i40e_ethdev.c          |   69 +-
 drivers/net/intel/i40e/i40e_ethdev.h          |   66 +-
 drivers/net/intel/i40e/i40e_fdir.c            |   78 +-
 drivers/net/intel/i40e/i40e_flow.c            | 4181 +----------------
 drivers/net/intel/i40e/i40e_flow.h            |   30 +
 drivers/net/intel/i40e/i40e_flow_ethertype.c  |  250 +
 drivers/net/intel/i40e/i40e_flow_fdir.c       | 1834 ++++++++
 drivers/net/intel/i40e/i40e_flow_hash.c       | 1298 +++++
 drivers/net/intel/i40e/i40e_flow_tunnel.c     | 1504 ++++++
 drivers/net/intel/i40e/i40e_hash.c            | 1037 +---
 drivers/net/intel/i40e/i40e_hash.h            |    9 +-
 drivers/net/intel/i40e/meson.build            |    4 +
 drivers/net/intel/i40e/rte_pmd_i40e.c         |    3 +-
 drivers/net/intel/ixgbe/ixgbe_ethdev.c        |   43 +-
 drivers/net/intel/ixgbe/ixgbe_ethdev.h        |   17 +-
 drivers/net/intel/ixgbe/ixgbe_fdir.c          |    1 -
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 3208 +------------
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   27 +
 .../net/intel/ixgbe/ixgbe_flow_ethertype.c    |  245 +
 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c     | 1553 ++++++
 drivers/net/intel/ixgbe/ixgbe_flow_hash.c     |  178 +
 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c    |  230 +
 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c   |  493 ++
 drivers/net/intel/ixgbe/ixgbe_flow_security.c |  320 ++
 drivers/net/intel/ixgbe/ixgbe_flow_syn.c      |  283 ++
 drivers/net/intel/ixgbe/ixgbe_ipsec.c         |   54 +-
 drivers/net/intel/ixgbe/ixgbe_ipsec.h         |    4 +-
 drivers/net/intel/ixgbe/meson.build           |    7 +
 lib/ethdev/meson.build                        |    1 +
 lib/ethdev/rte_flow_graph.h                   |  532 +++
 35 files changed, 11289 insertions(+), 8582 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/flow_graph.rst
 create mode 100644 drivers/net/intel/common/flow_engine.h
 create mode 100644 drivers/net/intel/common/flow_util.h
 create mode 100644 drivers/net/intel/i40e/i40e_flow.h
 create mode 100644 drivers/net/intel/i40e/i40e_flow_ethertype.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_fdir.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_hash.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_tunnel.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow.h
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ethertype.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_hash.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_security.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_syn.c
 create mode 100644 lib/ethdev/rte_flow_graph.h
Signature · 1 line
2.52.0
#1 lore raw

PATCH 1/21 ethdev: add flow graph API

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

---
Depends-on: series-39013 ("I40E refactors")

 doc/guides/prog_guide/ethdev/flow_graph.rst | 748 ++++++++++++++++++++
 doc/guides/prog_guide/ethdev/index.rst      |   1 +
 doc/guides/rel_notes/release_26_11.rst      |   7 +
 lib/ethdev/meson.build                      |   1 +
 lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++
 5 files changed, 1289 insertions(+)
 create mode 100644 doc/guides/prog_guide/ethdev/flow_graph.rst
 create mode 100644 lib/ethdev/rte_flow_graph.h
doc/guides/prog_guide/ethdev/flow_graph.rst new file +748 -0
new file mode 100644
index 0000000000..0b754a2a73
@@ -0,0 +1,748 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Intel Corporation
+
+Flow Graph Parser
+=================
+
+Introduction
+------------
+
+The flow graph parser is a helper library for PMD drivers that implements ``rte_flow`` pattern matching.
+It lets a driver declare the protocol sequences it supports as a directed graph of nodes and edges.
+It then validates and extracts fields from an ``rte_flow_item`` pattern in a single traversal.
+
+The library is defined in ``rte_flow_graph.h`` and is header-only.
+
+Scope and Limitations
+~~~~~~~~~~~~~~~~~~~~~
+
+Because the parser is graph-based, it is well suited for matching *protocol stacks*.
+These are sequences of protocol headers such as ``ETH / IPv4 / TCP``.
+Any pattern that can be expressed as "from protocol A, transitions to protocol B or C are allowed" fits naturally into the graph model.
+
+The library is **not** designed to cover every ``rte_flow`` item type.
+Items that do not represent a position in a protocol stack do not have a natural place in a protocol graph.
+This includes conntrack state, meter color, and other metadata items.
+Such items are best handled outside the graph, either before or after the graph parse call.
+
+Defining a Graph
+----------------
+
+A graph consists of three parts:
+
+1. An **enum** that assigns a numeric index to every node.
+2. A **node array** (``struct rte_flow_graph_node[]``) indexed by that enum.
+3. An **edge array** (``struct rte_flow_graph_edge[]``) also indexed by that enum, describing allowed transitions.
+
+These parts are bundled together in a ``struct rte_flow_graph``.
+
+The running example used throughout this guide models the following protocol graph::
+
+   START -> ETH -> [VLAN] -> (IPv4 | IPv6) -> [(TCP | UDP | SCTP)] -> END
+
+Brackets ``[...]`` denote optional items.
+Parentheses ``(...)`` denote a required choice between alternatives.
+The key ideas are:
+
+* ``ETH`` is required after ``START``.
+* After ``ETH``, an optional ``VLAN`` may appear, but the pattern must then see an IP layer.
+* After an IP layer, an optional transport layer may appear; it may be TCP, UDP, or SCTP, after which the pattern reaches ``END``.
+
+Node Enum
+~~~~~~~~~
+
+Every node needs a stable index.
+The first node **must** be at index ``RTE_FLOW_NODE_FIRST`` (which is 0).
+This is the *start node*.
+It is used only as a traversal anchor and must not carry callbacks.
+
+.. code-block:: c
+
+   enum example_node_id {
+       EXAMPLE_NODE_START = RTE_FLOW_NODE_FIRST,
+       EXAMPLE_NODE_ETH,
+       EXAMPLE_NODE_VLAN,
+       EXAMPLE_NODE_IPV4,
+       EXAMPLE_NODE_IPV6,
+       EXAMPLE_NODE_TCP,
+       EXAMPLE_NODE_UDP,
+       EXAMPLE_NODE_SCTP,
+       EXAMPLE_NODE_END,
+       /* keep last */
+       EXAMPLE_NODE_MAX,
+   };
+
+Node Definitions
+~~~~~~~~~~~~~~~~
+
+Each node maps to one ``rte_flow_item_type``.
+It can also carry a *validate* callback, a *process* callback, and a set of *constraints*.
+The the ``END`` node can also have callbacks to perform end-of-match processing.
+
+A minimal skeleton (callbacks and constraints are added in later sections):
+
+.. code-block:: c
+
+   const struct rte_flow_graph example_graph = {
+       .nodes = (struct rte_flow_graph_node[]){
+           [EXAMPLE_NODE_START] = {
+               .name = "START",
+               /* Start node: no type, no callbacks */
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .name  = "ETH",
+               .type  = RTE_FLOW_ITEM_TYPE_ETH,
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .name  = "VLAN",
+               .type  = RTE_FLOW_ITEM_TYPE_VLAN,
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .name  = "IPV4",
+               .type  = RTE_FLOW_ITEM_TYPE_IPV4,
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .name  = "IPV6",
+               .type  = RTE_FLOW_ITEM_TYPE_IPV6,
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .name  = "TCP",
+               .type  = RTE_FLOW_ITEM_TYPE_TCP,
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .name  = "UDP",
+               .type  = RTE_FLOW_ITEM_TYPE_UDP,
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .name  = "SCTP",
+               .type  = RTE_FLOW_ITEM_TYPE_SCTP,
+           },
+           [EXAMPLE_NODE_END] = {
+               .name  = "END",
+               .type  = RTE_FLOW_ITEM_TYPE_END,
+           },
+       },
+   };
+
+Edge Definitions
+~~~~~~~~~~~~~~~~
+
+Edges express which nodes may follow the current one.
+Every edge list is terminated by the ``RTE_FLOW_NODE_EDGE_END`` sentinel.
+All non-``END`` nodes **must** have an edge list.
+The ``END`` node itself does not need one.
+
+.. code-block:: c
+
+   const struct rte_flow_graph example_graph = {
+       .nodes = (struct rte_flow_graph_node[]){
+           /* ... same nodes as above ... */
+       },
+       .edges = (struct rte_flow_graph_edge[]){
+           [EXAMPLE_NODE_START] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_ETH,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_VLAN,
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+       },
+   };
+
+Reading the edges back:
+
+* From ``START``, the parser can only reach ``ETH``, which makes ``ETH`` required.
+* From ``ETH``, the parser can reach ``VLAN``, ``IPV4``, or ``IPV6``, which makes ``VLAN`` optional.
+* From ``IPV4`` or ``IPV6``, the parser can reach ``TCP``, ``UDP``, ``SCTP``, or ``END``, which makes the transport layer optional.
+
+Assembling the Graph
+~~~~~~~~~~~~~~~~~~~~
+
+With nodes and edges defined inline, assembling the graph is just a matter of
+combining the two arrays into a single compound literal:
+
+.. code-block:: c
+
+   const struct rte_flow_graph example_graph = {
+       .nodes = (struct rte_flow_graph_node[]){
+           [EXAMPLE_NODE_START] = {
+               .name = "START"
+           },
+           [EXAMPLE_NODE_ETH]   = {
+               .name = "ETH",
+               .type = RTE_FLOW_ITEM_TYPE_ETH
+           },
+           /* ... remaining nodes ... */
+           [EXAMPLE_NODE_END]   = {
+               .name = "END",
+               .type = RTE_FLOW_ITEM_TYPE_END
+           },
+       },
+       .edges = (struct rte_flow_graph_edge[]){
+           [EXAMPLE_NODE_START] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_ETH,
+                   RTE_FLOW_NODE_EDGE_END
+               }
+           },
+           /* ... remaining edges ... */
+       },
+   };
+
+Callbacks
+---------
+
+The graph calls up to two callbacks on every visited node: *validate* and *process*.
+
+Both callbacks share the same return convention.
+On success they must return ``0``.
+On failure they must call ``rte_flow_error_set`` to record a descriptive error and return its result.
+
+Validate Callback
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+   typedef int (*rte_flow_node_validate_fn)(
+       const void *ctx,
+       const struct rte_flow_item *item,
+       struct rte_flow_error *error);
+
+This callback receives a **read-only** context pointer.
+The canonical intent is that it should check whether the item's spec, mask, and last values are acceptable for the driver.
+On failure it returns the result of ``rte_flow_error_set`` (see the return convention above).
+
+It is recommended to use this callback for **all checks that can reject a rule**.
+This includes unsupported mask bits, conflicting field combinations, hardware limitations, and other applicable criteria.
+
+Process Callback
+~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+   typedef int (*rte_flow_node_process_fn)(
+       void *ctx,
+       const struct rte_flow_item *item,
+       struct rte_flow_error *error);
+
+This callback receives a **mutable** context pointer.
+The canonical expectation is that it should extract the fields needed for hardware programming.
+It should then store extracted data in the driver's context structure.
+On the rare failure path it returns the result of ``rte_flow_error_set`` (see the return convention above).
+
+It is recommended to use this callback for the **happy path**.
+For example, it can copy addresses, ports, and protocol IDs into the driver context so they can be programmed later.
+
+Defining a Context Structure
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The opaque ``ctx`` pointer passed to every callback is driver-defined.
+A typical context accumulates the parsed protocol fields:
+
+.. code-block:: c
+
+   struct example_parsed_flow {
+       /* L2 */
+       struct rte_ether_addr dst_mac;
+       bool has_vlan;
+       uint16_t vlan_tci;
+
+       /* L3 */
+       bool is_ipv6;
+       rte_be32_t ipv4_src;
+       rte_be32_t ipv4_dst;
+       uint8_t ipv6_src[16];
+       uint8_t ipv6_dst[16];
+
+       /* L4 */
+       enum rte_flow_item_type l4_proto;
+       rte_be16_t src_port;
+       rte_be16_t dst_port;
+   };
+
+These fields are meant to reflect the structure used by the driver to programming hardware with.
+
+Callback Example
+~~~~~~~~~~~~~~~~
+
+Below is a validate/process pair for the IPv4 node.
+The validate callback rejects unsupported mask bits.
+The process callback copies addresses into the context:
+
+.. code-block:: c
+
+   static int
+   example_validate_ipv4(const void *ctx __rte_unused,
+                         const struct rte_flow_item *item,
+                         struct rte_flow_error *error)
+   {
+       const struct rte_flow_item_ipv4 *mask = item->mask;
+
+       if (mask->hdr.version_ihl ||
+           mask->hdr.type_of_service ||
+           mask->hdr.total_length ||
+           mask->hdr.packet_id ||
+           mask->hdr.fragment_offset ||
+           mask->hdr.time_to_live ||
+           mask->hdr.next_proto_id ||
+           mask->hdr.hdr_checksum) {
+           return rte_flow_error_set(error, EINVAL,
+                   RTE_FLOW_ERROR_TYPE_ITEM, item,
+                   "Only src/dst addresses supported");
+       }
+       return 0;
+   }
+
+   static int
+   example_process_ipv4(void *ctx,
+                        const struct rte_flow_item *item,
+                        struct rte_flow_error *error __rte_unused)
+   {
+       struct example_parsed_flow *parsed = ctx;
+       const struct rte_flow_item_ipv4 *spec = item->spec;
+
+       parsed->is_ipv6 = false;
+       if (spec != NULL) {
+           parsed->ipv4_src = spec->hdr.src_addr;
+           parsed->ipv4_dst = spec->hdr.dst_addr;
+       }
+       return 0;
+   }
+
+Add the callbacks to the node definition:
+
+.. code-block:: c
+
+   [EXAMPLE_NODE_IPV4] = {
+       .name      = "IPV4",
+       .type      = RTE_FLOW_ITEM_TYPE_IPV4,
+       .validate  = example_validate_ipv4,
+       .process   = example_process_ipv4,
+   },
+
+Node Constraints
+----------------
+
+Many nodes share common requirements about which combination of ``spec``, ``mask``, and ``last`` pointers an item must carry.
+Instead of checking these in every validate callback, they can be declared via the ``constraints`` field.
+The field uses ``rte_flow_graph_node_expect`` flags.
+
+Available constraint flags (may be ORed together):
+
+``RTE_FLOW_NODE_EXPECT_EMPTY``
+   The item must have ``spec == NULL``, ``mask == NULL``, and
+   ``last == NULL``.
+
+``RTE_FLOW_NODE_EXPECT_SPEC``
+   ``spec`` is required; ``mask`` and ``last`` must be NULL.
+
+``RTE_FLOW_NODE_EXPECT_MASK``
+   ``mask`` is required; ``spec`` and ``last`` must be NULL.
+
+``RTE_FLOW_NODE_EXPECT_SPEC_MASK``
+   Both ``spec`` and ``mask`` are required; ``last`` must be NULL.
+
+``RTE_FLOW_NODE_EXPECT_RANGE``
+   All three (``spec``, ``mask``, ``last``) are required.
+
+``RTE_FLOW_NODE_EXPECT_NOT_RANGE``
+   ``last`` must be NULL (``spec`` and ``mask`` are unconstrained).
+
+Multiple flags can be ORed together.
+The item is accepted if **any one** of the flagged constraints is satisfied.
+
+For example, an IPv4 node that accepts either a mask-only item or a spec+mask item:
+
+.. code-block:: c
+
+   [EXAMPLE_NODE_IPV4] = {
+       .name        = "IPV4",
+       .type        = RTE_FLOW_ITEM_TYPE_IPV4,
+       .validate    = example_validate_ipv4,
+       .process     = example_process_ipv4,
+       .constraints = RTE_FLOW_NODE_EXPECT_MASK |
+                      RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+   },
+
+An Ethernet node that may appear empty (no spec/mask) or with spec+mask:
+
+.. code-block:: c
+
+   [EXAMPLE_NODE_ETH] = {
+       .name        = "ETH",
+       .type        = RTE_FLOW_ITEM_TYPE_ETH,
+       .validate    = example_validate_eth,
+       .process     = example_process_eth,
+       .constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+                      RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+   },
+
+Constraints are checked **before** the validate callback is invoked.
+
+Ignoring Item Types
+~~~~~~~~~~~~~~~~~~~
+
+The ``ignore_nodes`` field on ``struct rte_flow_graph`` is an optional complement to node constraints.
+When the pattern may contain item types that are irrelevant to the driver, list them in ``ignore_nodes``.
+For example, metadata items like ``RTE_FLOW_ITEM_TYPE_MARK`` do not represent a protocol header.
+The parser skips ignored items silently without advancing the current graph position:
+
+.. code-block:: c
+
+   const struct rte_flow_graph example_graph = {
+       /* ... nodes and edges ... */
+       .ignore_nodes = (const enum rte_flow_item_type[]){
+           RTE_FLOW_ITEM_TYPE_MARK,
+           RTE_FLOW_ITEM_TYPE_END,
+       },
+   };
+
+``RTE_FLOW_ITEM_TYPE_VOID`` is always ignored regardless of this list.
+Omit ``ignore_nodes`` entirely when no additional item types need to be skipped.
+
+Calling the Parser
+------------------
+
+``rte_flow_graph_parse`` walks the pattern against the graph:
+
+.. code-block:: c
+
+   int
+   rte_flow_graph_parse(const struct rte_flow_graph *graph,
+                        const struct rte_flow_item *pattern,
+                        struct rte_flow_error *error,
+                        void *ctx);
+
+A typical call site looks like this:
+
+.. code-block:: c
+
+   struct example_parsed_flow parsed;
+   int ret;
+
+   memset(&parsed, 0, sizeof(parsed));
+
+   ret = rte_flow_graph_parse(&example_graph, pattern, error, &parsed);
+   if (ret != 0)
+       return ret;
+
+   /* 'parsed' now contains the extracted protocol fields */
+
+The function returns success or failure, with ``error`` populated.
+
+Error conditions:
+
+* **Graph is NULL** — for example, when the graph pointer itself is not provided.
+* **Pattern is NULL**.
+* **Unsupported transition** — when an item type has no matching edge from the current node.
+    This is the primary way the graph rejects unsupported protocol sequences.
+* **Constraint failure** — when the spec, mask, and last combination does not satisfy the node's declared constraints.
+* **Validate callback failure** — when driver-specific validation rejects the item.
+* **Process callback failure** — when driver-specific extraction path fails.
+
+.. warning::
+
+    Malformed graph tables (for example invalid node indices, missing sentinels,
+    or otherwise inconsistent driver-defined graph structures) are considered to be a driver implementation bug.
+    Graphs are trusted by default: driver-owned graph structures are expected to be valid and are not fully validated.
+
+The traversal processes items in order, skipping ignored types.
+After the last non-``END`` item, the parser looks for an ``END`` node reachable from the current position.
+It then visits that node and runs its callbacks, if any.
+This means drivers can attach a process callback to the ``END`` node for post-traversal finalization.
+
+Putting It All Together
+-----------------------
+
+The complete graph definition with callbacks and constraints:
+
+.. code-block:: c
+
+   const struct rte_flow_graph example_graph = {
+       .nodes = (struct rte_flow_graph_node[]){
+           [EXAMPLE_NODE_START] = {
+               .name = "START",
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .name        = "ETH",
+               .type        = RTE_FLOW_ITEM_TYPE_ETH,
+               .validate    = example_validate_eth,
+               .process     = example_process_eth,
+               .constraints = RTE_FLOW_NODE_EXPECT_EMPTY
+                            | RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .name        = "VLAN",
+               .type        = RTE_FLOW_ITEM_TYPE_VLAN,
+               .process     = example_process_vlan,
+               .constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .name        = "IPV4",
+               .type        = RTE_FLOW_ITEM_TYPE_IPV4,
+               .validate    = example_validate_ipv4,
+               .process     = example_process_ipv4,
+               .constraints = RTE_FLOW_NODE_EXPECT_MASK
+                            | RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .name        = "IPV6",
+               .type        = RTE_FLOW_ITEM_TYPE_IPV6,
+               .validate    = example_validate_ipv6,
+               .process     = example_process_ipv6,
+               .constraints = RTE_FLOW_NODE_EXPECT_MASK
+                            | RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .name        = "TCP",
+               .type        = RTE_FLOW_ITEM_TYPE_TCP,
+               .validate    = example_validate_tcp,
+               .process     = example_process_tcp,
+               .constraints = RTE_FLOW_NODE_EXPECT_MASK
+                            | RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .name        = "UDP",
+               .type        = RTE_FLOW_ITEM_TYPE_UDP,
+               .validate    = example_validate_udp,
+               .process     = example_process_udp,
+               .constraints = RTE_FLOW_NODE_EXPECT_MASK
+                            | RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .name        = "SCTP",
+               .type        = RTE_FLOW_ITEM_TYPE_SCTP,
+               .process     = example_process_sctp,
+               .constraints = RTE_FLOW_NODE_EXPECT_EMPTY
+                            | RTE_FLOW_NODE_EXPECT_MASK
+                            | RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_END] = {
+               .name = "END",
+               .type = RTE_FLOW_ITEM_TYPE_END,
+           },
+       },
+       .edges = (struct rte_flow_graph_edge[]){
+           [EXAMPLE_NODE_START] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_ETH,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_VLAN,
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .next = (const size_t[]){
+                   EXAMPLE_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+       },
+   };
+
+
+Tunnel Graphs and Repeated Item Types
+-------------------------------------
+
+Tunneled patterns often repeat the same ``rte_flow_item_type`` in outer and inner headers.
+A simple representative example is a TCP-IPv4 over GTP-U pattern::
+
+   ETH -> IPV4 -> UDP -> GTPU -> IPV4 -> TCP
+
+The graph library supports this naturally.
+Multiple nodes may use the same ``type`` value, as long as they are distinct nodes in the graph and reached through different edges.
+
+For tunnel parsing, the recommended style is to model repeated protocol types as separate inner/outer nodes, for example ``OUTER_IPV4`` and ``INNER_IPV4``.
+This makes the graph intent explicit, keeps callback logic clear, and avoids unexpected graph paths due to loops.
+
+.. code-block:: c
+
+   enum tunnel_node_id {
+       TUNNEL_NODE_START = RTE_FLOW_NODE_FIRST,
+       TUNNEL_NODE_ETH,
+       TUNNEL_NODE_OUTER_IPV4,
+       TUNNEL_NODE_TCP,
+       TUNNEL_NODE_UDP,
+       TUNNEL_NODE_GTPU,
+       TUNNEL_NODE_INNER_IPV4,
+       TUNNEL_NODE_END,
+       TUNNEL_NODE_MAX,
+   };
+
+   const struct rte_flow_graph tunnel_graph = {
+       .nodes = (struct rte_flow_graph_node[]){
+           /* Minimal topology example: callbacks and constraints are omitted. */
+           [TUNNEL_NODE_START] = { .name = "START" },
+           [TUNNEL_NODE_ETH] = {
+               .name = "ETH",
+               .type = RTE_FLOW_ITEM_TYPE_ETH,
+           },
+           [TUNNEL_NODE_OUTER_IPV4] = {
+               .name = "OUTER_IPV4",
+               .type = RTE_FLOW_ITEM_TYPE_IPV4,
+           },
+           [TUNNEL_NODE_UDP] = {
+               .name = "UDP",
+               .type = RTE_FLOW_ITEM_TYPE_UDP,
+           },
+           [TUNNEL_NODE_GTPU] = {
+               .name = "GTPU",
+               .type = RTE_FLOW_ITEM_TYPE_GTPU,
+           },
+           [TUNNEL_NODE_INNER_IPV4] = {
+               .name = "INNER_IPV4",
+               .type = RTE_FLOW_ITEM_TYPE_IPV4,
+           },
+           [TUNNEL_NODE_TCP] = {
+               .name = "TCP",
+               .type = RTE_FLOW_ITEM_TYPE_TCP,
+           },
+           [TUNNEL_NODE_END] = {
+               .name = "END",
+               .type = RTE_FLOW_ITEM_TYPE_END,
+           },
+       },
+       .edges = (struct rte_flow_graph_edge[]){
+           [TUNNEL_NODE_START] = {
+               .next = (const size_t[]){
+                   TUNNEL_NODE_ETH,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_ETH] = {
+               .next = (const size_t[]){
+                   TUNNEL_NODE_OUTER_IPV4,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_OUTER_IPV4] = {
+               .next = (const size_t[]){
+                   TUNNEL_NODE_UDP,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_UDP] = {
+               .next = (const size_t[]){
+                   TUNNEL_NODE_GTPU,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_GTPU] = {
+               .next = (const size_t[]){
+                   TUNNEL_NODE_INNER_IPV4,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_INNER_IPV4] = {
+               .next = (const size_t[]){
+                   TUNNEL_NODE_TCP,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_TCP] = {
+               .next = (const size_t[]){
+                   TUNNEL_NODE_END,
+                   RTE_FLOW_NODE_EDGE_END,
+               },
+           },
+       },
+   };
+
+In other words, traversal follows graph edges (node-to-node), while node matching is done against each candidate node's ``rte_flow_item_type``.
+That combination allows repeated protocol layers to be represented cleanly with separate nodes for different parsing contexts.
+
+Although arbitrary loops are possible in the graph, tunnel protocol graphs are usually easier to reason about when repeated item types are split into explicit inner/outer nodes.
+It is not recommended to create loops in the graph, as these loops will be unbounded.
doc/guides/prog_guide/ethdev/index.rst +1 -0
index 392ced0a2e..b41fd045bb 100644
@@ -10,6 +10,7 @@ Ethernet Device Library
     ethdev
     switch_representation
     flow_offload
+    flow_graph
     traffic_metering_and_policing
     traffic_management
     qos_framework
doc/guides/rel_notes/release_26_11.rst +7 -0
index c8cc86295d..a4235d7dac 100644
@@ -55,6 +55,13 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added internal ethdev flow graph parser helper API.**
+
+  Added ``rte_flow_graph`` helper definitions in ``rte_flow_graph.h``
+  for PMD drivers to build graph-based pattern parsers.
+  This internal driver API validates ``rte_flow_item`` protocol sequences
+  through node and edge traversal, with per-node callbacks and constraints.
+
 
 Removed Items
 -------------
lib/ethdev/meson.build +1 -0
index 8ba6c708a2..686b64e3c2 100644
@@ -40,6 +40,7 @@ driver_sdk_headers += files(
         'ethdev_pci.h',
         'ethdev_vdev.h',
         'rte_flow_driver.h',
+        'rte_flow_graph.h',
         'rte_mtr_driver.h',
         'rte_tm_driver.h',
 )
lib/ethdev/rte_flow_graph.h new file +532 -0

532 lines not on this page · raw message

Signature · 1 line
2.52.0
#2 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

[...]

Quoted 1 line
 lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++

Are we sure about the name? Why not "parsing" instead of "graph"?

Is it only for drivers? In this case, no need to make it a public API.

#3 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

Quoted 10 lines · Thomas Monjalon

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

Quoted 5 lines

[...]

Quoted 1 line
  lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++

Are we sure about the name? Why not "parsing" instead of "graph"?

Because it is intended to help build *protocol graphs*, not *generic flow parsing*. I.e. only things that lend themselves to be represented by a graph would work with this - things like conntrack wouldn't. So, I feel like "graph" is a more accurate name for this, because this is only for enabling the protocol graph walk, not for full flow parsing.

Quoted 2 lines

Is it only for drivers? In this case, no need to make it a public API.

Yes, it is for drivers. It is not a public API, it is not installed into public headers. If there is anything I forgot to do to remove it from being public, please let me know.

Signature · 2 lines
Thanks,
Anatoly
#4 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

26/08/2026 10:58, Burakov, Anatoly:

Quoted 14 lines · Thomas Monjalon
Quoted 13 lines

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

[...]

Quoted 1 line
  lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++
Quoted 8 lines
Quoted 2 lines

Are we sure about the name? Why not "parsing" instead of "graph"?

Because it is intended to help build *protocol graphs*, not *generic flow parsing*. I.e. only things that lend themselves to be represented by a graph would work with this - things like conntrack wouldn't. So, I feel like "graph" is a more accurate name for this, because this is only for enabling the protocol graph walk, not for full flow parsing.

OK

Quoted 5 lines
Quoted 1 line

Is it only for drivers? In this case, no need to make it a public API.

Yes, it is for drivers. It is not a public API, it is not installed into public headers. If there is anything I forgot to do to remove it from being public, please let me know.

You should not use the rte_ prefix for files which are not public. I know this convention is not well followed for historical reason, like rte_flow_driver.h should be flow_driver.h

#5 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

Quoted 28 lines · Thomas Monjalon

26/08/2026 10:58, Burakov, Anatoly:

Quoted 14 lines · Thomas Monjalon
Quoted 13 lines

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

[...]

Quoted 1 line
  lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++
Quoted 8 lines
Quoted 2 lines

Are we sure about the name? Why not "parsing" instead of "graph"?

Because it is intended to help build *protocol graphs*, not *generic flow parsing*. I.e. only things that lend themselves to be represented by a graph would work with this - things like conntrack wouldn't. So, I feel like "graph" is a more accurate name for this, because this is only for enabling the protocol graph walk, not for full flow parsing.

OK

Quoted 2 lines
Quoted 1 line

Is it only for drivers? In this case, no need to make it a public API.

Quoted 8 lines
Quoted 3 lines

Yes, it is for drivers. It is not a public API, it is not installed into public headers. If there is anything I forgot to do to remove it from being public, please let me know.

You should not use the rte_ prefix for files which are not public. I know this convention is not well followed for historical reason, like rte_flow_driver.h should be flow_driver.h

Renaming non-public files should not be an issue though, right? I realise it may cause a couple of problems with backporting, but hopefully not too many. LTS maintainers, feel free to weigh in!

/Bruce

#6 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

In reply to Thomas Monjalon
Quoted 28 lines · Thomas Monjalon

26/08/2026 10:58, Burakov, Anatoly:

Quoted 14 lines · Thomas Monjalon
Quoted 13 lines

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

[...]

Quoted 1 line
   lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++
Quoted 8 lines
Quoted 2 lines

Are we sure about the name? Why not "parsing" instead of "graph"?

Because it is intended to help build *protocol graphs*, not *generic flow parsing*. I.e. only things that lend themselves to be represented by a graph would work with this - things like conntrack wouldn't. So, I feel like "graph" is a more accurate name for this, because this is only for enabling the protocol graph walk, not for full flow parsing.

OK

Quoted 2 lines
Quoted 1 line

Is it only for drivers? In this case, no need to make it a public API.

Quoted 9 lines
Quoted 3 lines

Yes, it is for drivers. It is not a public API, it is not installed into public headers. If there is anything I forgot to do to remove it from being public, please let me know.

You should not use the rte_ prefix for files which are not public. I know this convention is not well followed for historical reason, like rte_flow_driver.h should be flow_driver.h

The fact that it didn't look like there was no convention was why I picked rte_ prefix. I'll happily use a non-rte prefix for these. Maybe use eth_ prefix for both flow_graph and flow_driver? Or does flow_graph work fine?

[on a separate note: should rte_vfio follow suit and be renamed? it is no longer a public API following my patchset. renaming it to vfio would probably cause conflicts with Linux headers, so maybe something like vfio_driver like flow_driver?]

Signature · 2 lines
Thanks,
Anatoly
#7 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

26/08/2026 12:20, Burakov, Anatoly:

Quoted 39 lines · Thomas Monjalon
Quoted 28 lines

26/08/2026 10:58, Burakov, Anatoly:

Quoted 14 lines · Thomas Monjalon
Quoted 13 lines

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

[...]

Quoted 1 line
   lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++
Quoted 8 lines
Quoted 2 lines

Are we sure about the name? Why not "parsing" instead of "graph"?

Because it is intended to help build *protocol graphs*, not *generic flow parsing*. I.e. only things that lend themselves to be represented by a graph would work with this - things like conntrack wouldn't. So, I feel like "graph" is a more accurate name for this, because this is only for enabling the protocol graph walk, not for full flow parsing.

OK

Quoted 2 lines
Quoted 1 line

Is it only for drivers? In this case, no need to make it a public API.

Quoted 9 lines
Quoted 3 lines

Yes, it is for drivers. It is not a public API, it is not installed into public headers. If there is anything I forgot to do to remove it from being public, please let me know.

You should not use the rte_ prefix for files which are not public. I know this convention is not well followed for historical reason, like rte_flow_driver.h should be flow_driver.h

Quoted 4 lines

The fact that it didn't look like there was no convention was why I picked rte_ prefix. I'll happily use a non-rte prefix for these. Maybe use eth_ prefix for both flow_graph and flow_driver? Or does flow_graph work fine?

I don't think adding eth prefix is needed.

Quoted 4 lines

[on a separate note: should rte_vfio follow suit and be renamed? it is no longer a public API following my patchset. renaming it to vfio would probably cause conflicts with Linux headers, so maybe something like vfio_driver like flow_driver?]

Yes vfio_driver looks fine.

#8 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

Quoted 45 lines · Thomas Monjalon

26/08/2026 12:20, Burakov, Anatoly:

Quoted 39 lines · Thomas Monjalon
Quoted 28 lines

26/08/2026 10:58, Burakov, Anatoly:

Quoted 14 lines · Thomas Monjalon
Quoted 13 lines

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

[...]

Quoted 1 line
    lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++
Quoted 8 lines
Quoted 2 lines

Are we sure about the name? Why not "parsing" instead of "graph"?

Because it is intended to help build *protocol graphs*, not *generic flow parsing*. I.e. only things that lend themselves to be represented by a graph would work with this - things like conntrack wouldn't. So, I feel like "graph" is a more accurate name for this, because this is only for enabling the protocol graph walk, not for full flow parsing.

OK

Quoted 2 lines
Quoted 1 line

Is it only for drivers? In this case, no need to make it a public API.

Quoted 9 lines
Quoted 3 lines

Yes, it is for drivers. It is not a public API, it is not installed into public headers. If there is anything I forgot to do to remove it from being public, please let me know.

You should not use the rte_ prefix for files which are not public. I know this convention is not well followed for historical reason, like rte_flow_driver.h should be flow_driver.h

Quoted 4 lines

The fact that it didn't look like there was no convention was why I picked rte_ prefix. I'll happily use a non-rte prefix for these. Maybe use eth_ prefix for both flow_graph and flow_driver? Or does flow_graph work fine?

Quoted 10 lines

I don't think adding eth prefix is needed.

Quoted 4 lines

[on a separate note: should rte_vfio follow suit and be renamed? it is no longer a public API following my patchset. renaming it to vfio would probably cause conflicts with Linux headers, so maybe something like vfio_driver like flow_driver?]

Yes vfio_driver looks fine.

Actually, on second thought, "vfio_driver" is too similar to "bus_driver" and "dev_driver" which kind of implies that "vfio_driver" is for writing vfio drivers. Maybe dev_vfio would be better?

(flow_driver sits in lib/ethdev which is a driver subsystem, but vfio sits in EAL which is not, so for flow_driver I think "flow_driver" is OK, but vfio header is in common include together with bus_driver and dev_driver, I think vfio_driver would be confusing)

Signature · 2 lines
Thanks,
Anatoly
#9 lore raw

Re: [PATCH v1 01/21] ethdev: add flow graph API

27/08/2026 09:58, Burakov, Anatoly:

Quoted 57 lines · Thomas Monjalon
Quoted 45 lines

26/08/2026 12:20, Burakov, Anatoly:

Quoted 39 lines · Thomas Monjalon
Quoted 28 lines

26/08/2026 10:58, Burakov, Anatoly:

Quoted 14 lines · Thomas Monjalon
Quoted 13 lines

20/08/2026 16:00, Anatoly Burakov:

Quoted 9 lines

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

[...]

Quoted 1 line
    lib/ethdev/rte_flow_graph.h                 | 532 ++++++++++++++
Quoted 8 lines
Quoted 2 lines

Are we sure about the name? Why not "parsing" instead of "graph"?

Because it is intended to help build *protocol graphs*, not *generic flow parsing*. I.e. only things that lend themselves to be represented by a graph would work with this - things like conntrack wouldn't. So, I feel like "graph" is a more accurate name for this, because this is only for enabling the protocol graph walk, not for full flow parsing.

OK

Quoted 2 lines
Quoted 1 line

Is it only for drivers? In this case, no need to make it a public API.

Quoted 9 lines
Quoted 3 lines

Yes, it is for drivers. It is not a public API, it is not installed into public headers. If there is anything I forgot to do to remove it from being public, please let me know.

You should not use the rte_ prefix for files which are not public. I know this convention is not well followed for historical reason, like rte_flow_driver.h should be flow_driver.h

Quoted 4 lines

The fact that it didn't look like there was no convention was why I picked rte_ prefix. I'll happily use a non-rte prefix for these. Maybe use eth_ prefix for both flow_graph and flow_driver? Or does flow_graph work fine?

Quoted 10 lines

I don't think adding eth prefix is needed.

Quoted 4 lines

[on a separate note: should rte_vfio follow suit and be renamed? it is no longer a public API following my patchset. renaming it to vfio would probably cause conflicts with Linux headers, so maybe something like vfio_driver like flow_driver?]

Yes vfio_driver looks fine.

Quoted 8 lines

Actually, on second thought, "vfio_driver" is too similar to "bus_driver" and "dev_driver" which kind of implies that "vfio_driver" is for writing vfio drivers. Maybe dev_vfio would be better?

(flow_driver sits in lib/ethdev which is a driver subsystem, but vfio sits in EAL which is not, so for flow_driver I think "flow_driver" is OK, but vfio header is in common include together with bus_driver and dev_driver, I think vfio_driver would be confusing)

OK

#10 lore raw

PATCH 2/21 net/intel/common: add flow engines infrastructure

In reply to Anatoly Burakov

Current implementation of flow engines in various drivers have a few issues that need to be corrected.

For one, some of the are fundamentally incompatible with secondary processes, because the flow engine registration and creation will allocate structures in shared memory but use process-local pointers to point to flow engines and pattern tables.

For another, a lot of them are needlessly complicated and rely on a separation between patterns and parsing that is hard to reason about and
maintain: they do not define memory ownership model, they do not define the way in which we approach parameter and pattern parsing, and they occasionally do weird things like passing around pointers-to-void-pointers or even using pointers as integer values.

These issues can be corrected, but because of how much code there is to the current infrastructure and how tightly coupled it is, it would be easier to just build new one from scratch, and gradually migrate all engines to use it. This patch is intended as a first step towards that goal, and defines both common data types to be used by all rte_flow parsers, as well as the interaction model that is to be followed by all drivers.

We define a set of structures that will represent:

- Defined rte_flow parsing interaction model and code flow (ops struct)
- Defined memory allocation and ownership model for all engines
- Scratch space format for all engines (variably allocated typed struct)
- Flow rule format for all engines (variably allocated typed struct)
- Engine definitions that are compatible with secondary process model
- Implementations of common rte_flow operations
- Various supporting infrastructure for parser customization, e.g. hooks
- Support for using custom allocation (e.g. for mempool-based alloc)

The design intent is heavily documented right inside the header and is to be considered authoritative design document for how to build rte_flow parsers for Intel Ethernet drivers going forward.

---
 drivers/net/intel/common/flow_engine.h | 1373 ++++++++++++++++++++++++
 1 file changed, 1373 insertions(+)
 create mode 100644 drivers/net/intel/common/flow_engine.h
drivers/net/intel/common/flow_engine.h new file +1373 -0
new file mode 100644
index 0000000000..7d705a5b2c
@@ -0,0 +1,1373 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef _COMMON_INTEL_FLOW_ENGINE_H_
+#define _COMMON_INTEL_FLOW_ENGINE_H_
+
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/queue.h>
+
+#include <rte_malloc.h>
+
+#include <ethdev_driver.h>
+#include <rte_flow.h>
+#include <rte_flow_graph.h>
+#include <rte_tailq.h>
+#include <rte_rwlock.h>
+#include <rte_hexdump.h>
+
+#include "log.h"
+
+/*
+ * This is a common header for Intel Ethernet drivers' flow engine
+ * implementations. It defines the interfaces and data structures required to
+ * implement flow rule engines that can be plugged into the drivers' flow
+ * handling logic.
+ *
+ * Design considerations:
+ *
+ * 1. Ease of implementation
+ *
+ * The flow engine interface is designed to be as simple as possible with
+ * obvious defaults (i.e. not specifying something leads to behavior that
+ * would've been the most expected in context). The point is not to produce a
+ * monstrous driver-within-a-driver framework, but rather to make engine
+ * definitions follow semantic expectations of what the engine actually does.
+ *
+ * All the boilerplate (flow management, engine enablement tracking, etc.) is
+ * handled by the common flow infrastructure, so the engine implementation only
+ * needs to focus on the actual logic of parsing and installing/uninstalling
+ * flow rules, and defining each step of the process as it pertains to each flow
+ * engine.
+ *
+ * It is expected that drivers will use other utility functions from the common
+ * flow-related code where applicable (e.g. flow_util.h, flow_check.h, etc.),
+ * however this is obviously up to each individual driver to handle.
+ *
+ * 2. Full secondary process compatibility
+ *
+ * In order to support rte_flow operations in secondary processes, we need to
+ * store which engines are enabled for particular driver instance, and resolve
+ * them at runtime. The engine index (its position in the engine list) is used as a
+ * bitshift-mask into a driver-specific 64-bit field of enabled engines. This
+ * way, the engine definitions can be stored in read-only memory, and referenced
+ * by both primary and secondary processes without issues.
+ *
+ * For this to remain safe, flow engine lists and engine definitions must be
+ * immutable for process lifetime (declare them as const).
+ *
+ * Note that this does not imply that all drivers are therefore able to support
+ * rte_flow-related operations in secondary processes - that is still up to each
+ * driver to implement. This just ensures that the flow engine framework does
+ * not prevent it.
+ *
+ * The per-instance engine configuration (enablement bitmask and per-engine
+ * private data) is set up and torn down exclusively by
+ * ci_flow_engine_conf_init() and ci_flow_engine_conf_reset(). These run the
+ * engine_init/engine_uninit callbacks and mutate state shared with secondary
+ * processes, so they are primary-process operations: a secondary process must
+ * never call them. Doing so is a driver bug that clobbers shared state.
+ *
+ * 3. Flow object lifecycle is framework-owned
+ *
+ * Engines are expected to treat framework-provided context and flow objects as
+ * storage they fill in, not storage they own. In other words, engine logic
+ * should focus on contents of flow data, while object lifetime is managed by
+ * the framework.
+ *
+ * Engines may still allocate auxiliary data (for per-engine private state or
+ * pointers stored inside flows), but all such allocations must be from shared
+ * memory and should follow engine_init/engine_uninit or flow lifetime as
+ * appropriate.
+ *
+ * 4. Pattern parsing: rte_flow_graph and pattern_parse callback
+ *
+ * The flow engine framework is designed to work hand-in-hand with the
+ * rte_flow_graph parsing infrastructure. Each engine may provide a pattern
+ * graph that is used to match the flow pattern, and extract relevant data
+ * into the engine context. This allows for cleaner separation of concerns,
+ * where the engine focuses on handling actions and attributes, while the
+ * graph parser deals with the pattern matching.
+ *
+ * Engines may also provide a `pattern_parse` callback that is invoked before
+ * the graph parser runs. This allows engines to handle pattern items that
+ * don't fit neatly into the graph model (e.g. FUZZY items that can appear at
+ * any position), as well as ignoring the graph parser entirely and implementing
+ * custom pattern parsing.
+ *
+ * If neither the graph nor the callback is provided, a default empty pattern
+ * graph is used that matches either [END], or [ANY] -> [END] patterns. A NULL
+ * pattern is also allowed in such case.
+ *
+ * NULL pattern handling therefore follows directly from mode selection:
+ *
+ * - empty fallback accepts NULL pattern
+ * - all other modes require non-NULL pattern
+ *
+ * Pattern matching mode summary:
+ *
+ * - graph only: graph != NULL, pattern_parse == NULL
+ * - callback only: graph == NULL, pattern_parse != NULL
+ * - callback + graph: graph != NULL, pattern_parse != NULL
+ * - empty fallback: graph == NULL, pattern_parse == NULL
+ *
+ * In callback + graph mode, the callback does not remove items from the
+ * original pattern stream. The graph parser still sees the same pattern, so the
+ * graph should define its `ignore_nodes` list appropriately.
+ *
+ * There is no way to completely ignore pattern contents for the engine except
+ * for defining a noop `pattern_parse` callback. This is by design, as such case
+ * is considered rte_flow API misuse. By default, even for empty fallback case,
+ * a meaningful pattern (one that is not empty or ANY) will be treated as error.
+ */
+
+/* forward declarations for flow engine data types */
+struct ci_flow_engine_ops;
+struct ci_flow_engine_ctx;
+struct ci_flow_engine;
+struct ci_flow_engine_ref;
+struct ci_flow_engine_list;
+struct ci_flow_engine_conf;
+struct ci_flow;
+
+/*
+ * Flow engine ops.
+ *
+ * Each flow engine must provide a set of operations to handle common
+ * operations, such as:
+ *
+ * - Initialize and clean up engine resources (engine_init/engine_uninit)
+ * - Allocate memory for flow rules (flow_alloc)
+ * - Parse flow attributes and actions into engine-specific context (ctx_parse)
+ * - Parse pattern into engine-specific context (pattern_parse)
+ * - Pattern graph to use when parsing flow patterns (see `ci_flow_engine`)
+ * - Validate the parsed attributes and actions against the data parsed from
+ *   pattern (ctx_validate)
+ * - Build the actual flow rule structure from the parsed context (ctx_to_flow)
+ * - Add/remove the flow rule to/from hardware or driver's internal state
+ *   (flow_install/flow_uninstall)
+ * - Query data for the flow rule (flow_query)
+ *
+ * The intended flow and semantics is as follows:
+ *
+ * - at init time:
+ *   [engine_init]
+ *
+ * - at reset/uninit time:
+ *   [engine_uninit]
+ *
+ * - at rte_flow_validate time:
+ *   ctx_parse -> [pattern_parse] -> [graph parser] -> [ctx_validate] -> [ctx_to_flow]
+ *
+ * - at rte_flow_create time:
+ *   [flow_alloc] -> ctx_parse -> [pattern_parse] -> [graph parser] -> [ctx_validate] -> [ctx_to_flow] -> [flow_install]
+ *
+ * - at rte_flow_destroy/rte_flow_flush time:
+ *   [flow_uninstall] -> [flow_free]
+ *
+ * - at rte_flow_query time:
+ *   [flow_query]
+ *
+ * 1) Engine availability and lifecycle
+ *
+ * The engine availability can be checked by the driver at init time. The exact
+ * mechanics of this is left up to each individual driver - it may be hardware
+ * capability bits, PHY type check, devargs, or any other criteria that makes
+ * sense in the context of driver/adapter. If the engine_init callback is not
+ * implemented, the engine is assumed to be always available.
+ *
+ * If `priv_size` is non-zero, the framework allocates a zeroed private block
+ * and passes it to engine_init/engine_uninit. Each allocated flow will also
+ * carry a pointer to this per-device private data in `flow->engine_priv`.
+ *
+ * 2) Input parsing
+ *
+ * `ctx_parse` is mandatory and acts as the main gateway for parsing actions and
+ * attributes into engine context.
+ *
+ * Patterns are matched either against the pattern graph provided by the
+ * `ci_flow_engine`, or by the `pattern_parse` callback, or both.
+ *
+ * Pattern handling mode is selected from graph/callback presence:
+ *
+ * - graph only: graph parser runs
+ * - callback only: pattern_parse runs
+ * - callback + graph: pattern_parse runs, then graph parser runs
+ * - neither graph nor callback: empty fallback matcher is used
+ *
+ * Empty fallback matcher accepts:
+ *
+ * - NULL pattern
+ * - empty pattern (start -> end)
+ * - ANY-only pattern (start -> any -> end)
+ *
+ * Any other patterns will be rejected by the empty path.
+ *
+ * 3) Consistency check
+ *
+ * `ctx_validate` is an optional final check for consistency between parsed
+ * action/attribute data and pattern-derived data.
+ *
+ * 4) Rule materialization
+ *
+ * `ctx_to_flow` translates parsed context into the concrete engine-specific
+ * flow rule representation. As a general guideline, flow rule should not
+ * contain anything that isn't useful for rule installation or querying. All
+ * of the temporary data should be stored in the engine context, while the
+ * rule should only contain data that is pertinent to flow programming.
+ *
+ * 5) Rule lifecycle hooks:
+ *
+ * `flow_alloc`/`flow_free` are optional custom object lifecycle callbacks.
+ * If `flow_alloc` is not provided (or returns NULL), the framework falls back
+ * to rte_zmalloc-based allocation. If allocation callback exists, `flow_free`
+ * must exist as well.
+ *
+ * Contract: flow_alloc/flow_free are allocation-policy callbacks only. They
+ * may allocate memory and update allocator-associated state, but must not
+ * produce parse-time semantics that engine callbacks depend on.
+ *
+ * In particular, ctx_parse/pattern_parse/ctx_validate/ctx_to_flow must remain
+ * correct without any flow_alloc-specific side effects (e.g. on validate path).
+ *
+ * The engines only own engine-specific fields. The common `ci_flow` fields
+ * (engine_idx, engine_priv, fallback_alloc, dev_data, node) are owned and
+ * managed by the framework.
+ *
+ * `flow_install`/`flow_uninstall` are optional hooks that synchronize accepted
+ * rules with hardware and/or driver internal state. If the engine requires
+ * per-flow allocations, this is the place to make them.
+ *
+ * IMPORTANT: by the time flow_install is called, the engine has already
+ * accepted the flow through ctx_parse/ctx_validate/ctx_to_flow. A failure in
+ * flow_install (e.g. hardware resource exhaustion) is treated as a hard
+ * failure: no other engines will be tried. It is therefore implied that if the
+ * flow parser has accepted the flow, it should in principle be installable -
+ * any checks that would prevent that from happening that are intrinsic to the
+ * pattern itself should have been done at parse/validate stage.
+ *
+ * 6) Flow query
+ *
+ * For querying data, the flow_query function is provided to query data for the
+ * flow rule. The engine may not provide this function, in which case any
+ * attempt to query the rule will result in failure.
+ *
+ * 7) Concurrency model
+ *
+ * The framework serializes access to each driver instance's flow state with a
+ * single rwlock. Create, destroy and flush take it exclusively (write);
+ * validate and query take it shared (read). Consequently the parse-phase
+ * callbacks (ctx_parse, pattern_parse, ctx_validate, ctx_to_flow) can run
+ * concurrently with each other on the validate path, and flow_query can run
+ * concurrently with other queries. These callbacks must treat shared engine
+ * state - including engine_priv - as read-only and must not mutate it. State
+ * mutation belongs in the write-locked callbacks (engine_init/engine_uninit,
+ * flow_alloc/flow_free, flow_install/flow_uninstall).
+ */
+struct ci_flow_engine_ops {
+	/* engine init callback - can be NULL */
+	int (*engine_init)(const struct ci_flow_engine *engine,
+			struct rte_eth_dev_data *dev_data,
+			void *priv);
+	/* engine uninit callback - can be NULL */
+	void (*engine_uninit)(const struct ci_flow_engine *engine,
+			void *priv);
+	/* allocation callback for flow rules - can be NULL */
+	struct ci_flow *(*flow_alloc)(const struct ci_flow_engine *engine,
+			struct rte_eth_dev_data *dev_data,
+			void *priv);
+	/* deallocation callback for flow rules - can be NULL */
+	void (*flow_free)(struct ci_flow *flow,
+			struct rte_eth_dev_data *dev_data,
+			void *priv);
+	/* initialize engine context from flow attr/actions - mandatory */
+	int (*ctx_parse)(const struct rte_flow_action actions[],
+			const struct rte_flow_attr *attr,
+			struct ci_flow_engine_ctx *ctx,
+			struct rte_flow_error *error);
+	/* pattern parsing callback - can be NULL */
+	int (*pattern_parse)(struct ci_flow_engine_ctx *ctx,
+			const struct rte_flow_item pattern[],
+			struct rte_flow_error *error);
+	/* final pass before converting context to flow - can be NULL */
+	int (*ctx_validate)(struct ci_flow_engine_ctx *ctx,
+			struct rte_flow_error *error);
+	/* initialize flow rule from parsed context - can be NULL */
+	int (*ctx_to_flow)(const struct ci_flow_engine_ctx *ctx,
+			struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* install a flow rule - can be NULL */
+	int (*flow_install)(struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* uninstall a flow rule - can be NULL */
+	int (*flow_uninstall)(struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* query flow - can be NULL */
+	int (*flow_query)(struct ci_flow *flow,
+			const struct rte_flow_action *action,
+			void *data,
+			struct rte_flow_error *error);
+};
+
+/*
+ * common definition for flow engine context.
+ * each engine will define its own context structure that
+ * *must* start with this base structure.
+ */
+struct ci_flow_engine_ctx {
+	/* ethernet device this context belongs to */
+	struct rte_eth_dev_data *dev_data;
+};
+
+/*
+ * Common definition for flow rule.
+ *
+ * For flow rules, there are three parts to consider:
+ *
+ * 1) Common data
+ * 2) Driver-specific data
+ * 3) Engine-specific data
+ *
+ * The common data is defined here as the `ci_flow` structure. It contains
+ * fields that are common to all flow rules, regardless of driver or engine.
+ * This includes a linked list node for managing flow rules in a list, a pointer
+ * to the device (driver instance) the flow belongs to, and the engine index
+ * that created the flow.
+ *
+ * With rte_flow API, each driver is meant to define its own rte_flow structure
+ * that contains driver-specific data. This structure must start with the
+ * `ci_flow` structure defined here, followed by driver-specific fields.
+ *
+ * Additionally, each *engine* may want to define its own flow rule structure
+ * that contains actual engine-specific data. This structure must start with the
+ * driver-wide `rte_flow` structure such that it contains everything before it,
+ * followed by engine-specific fields.
+ *
+ * IMPORTANT:
+ *
+ * All of these structures will be referred to by the same pointer and can be
+ * freely (and safely) cast between each other *as long as* each structure
+ * definition has the parent structure as its first member. E.g. the common flow
+ * struct is `ci_flow`, and the driver-specific `rte_flow` must be defined as
+ * follows:
+ *
+ * struct rte_flow {
+ *     struct ci_flow base;
+ *     ...any driver-specific fields...
+ * }
+ *
+ * If the engine needs to define its own flow structure, in turn it should be
+ * defined as follows:
+ *
+ * struct ixgbe_fdir_flow {
+ *     struct rte_flow base;
+ *     ...any engine-specific fields...
+ * }
+ *
+ * This ensures pointer conversion safety between all three types:
+ *
+ *     struct ci_flow *flow = ...;
+ *     struct rte_flow *rte_flow = (struct rte_flow *)flow;
+ *     struct ixgbe_fdir_flow *fdir_flow = (struct ixgbe_fdir_flow *)flow;
+ *
+ * The engine structure provides a `flow_size` field that indicates how much
+ * memory is required for a particular engine's flow structure. The driver must
+ * provide that value for each engine, as it will be used to size flow structure
+ * allocations. If the engine does not require any memory beyond the `rte_flow`
+ * structure, this value should be set to `sizeof(rte_flow)` for those engines.
+ *
+ * Engine references and engine_idx:
+ *
+ * The engine index is treated as the engine type discriminator and is stored in
+ * every flow. Runtime code then reconstructs a `ci_flow_engine_ref`
+ * (engine pointer + index) from the immutable engine list and this stored
+ * index. This gives two important guarantees:
+ *
+ * - The framework works only with immutable engine definitions/lists.
+ * - We avoid repeated pointer-to-index lookups after flow creation; rebuilding
+ *   a ref from index is direct and keeps call sites explicit about both values.
+ *
+ * This is particularly important for secondary-process compatibility where the
+ * index is the stable identity and pointer values are only meaningful in the
+ * context of the shared immutable engine list.
+ *
+ * Code that works with engine pointers and engine indices should be careful,
+ * and should treat engine references as the basic building block.
+ *
+ * Per-engine private data (engine_priv):
+ *
+ * Each flow will also carry a pointer to engine-private data, to allow for
+ * reaching engine-specific data, such as custom allocation structures to
+ * be used with flow_alloc/flow_free at allocation time.
+ */
+struct ci_flow {
+	TAILQ_ENTRY(ci_flow) node;
+	/* device this flow belongs to */
+	struct rte_eth_dev_data *dev_data;
+	/* index of engine this flow was created by */
+	size_t engine_idx;
+	/* per-engine private data pointer, set by the framework at alloc time */
+	void *engine_priv;
+	/* set if engine allocator callback existed but fallback allocator was used */
+	bool fallback_alloc;
+};
+
+/* flow engine definition */
+struct ci_flow_engine {
+	/* engine name */
+	const char *name;
+	/* size of scratch space structure, can be 0 */
+	size_t ctx_size;
+	/* size of flow rule structure, must not be 0 */
+	size_t flow_size;
+	/* size of per-device engine private data, can be 0 */
+	size_t priv_size;
+	/* ops for this flow engine */
+	const struct ci_flow_engine_ops *ops;
+	/* pattern graph this engine supports - can be NULL */
+	const struct rte_flow_graph *graph;
+};
+
+/* engine reference: immutable engine pointer paired with its list index */
+struct ci_flow_engine_ref {
+	const struct ci_flow_engine *engine;
+	size_t engine_idx;
+};
+
+#define CI_FLOW_ENGINE_MAX	64
+
+/* flow engine list definition */
+struct ci_flow_engine_list {
+	/* NULL-terminated immutable array of flow engine pointers */
+	const struct ci_flow_engine * const engines[CI_FLOW_ENGINE_MAX];
+};
+
+/* flow engine configuration - each device must have its own instance */
+struct ci_flow_engine_conf {
+	/* lock to protect config */
+	rte_rwlock_t config_lock;
+	/* list of flows created on this device */
+	TAILQ_HEAD(ci_flow_list, ci_flow) flows;
+	/* bitmask of enabled engines */
+	uint64_t enabled_engines;
+	/* back-reference to device structure */
+	struct rte_eth_dev_data *dev_data;
+	/* reference to the driver's engine list */
+	const struct ci_flow_engine_list *engines;
+	/* per-engine private data pointers, indexed by engine index */
+	void *engine_priv[CI_FLOW_ENGINE_MAX];
+};
+
+/* build engine ref from engine config and engine index - thread-unsafe */
+static inline struct ci_flow_engine_ref
+ci_flow_engine_ref_from_idx(const struct ci_flow_engine_conf *engine_conf,
+		const size_t engine_idx)
+{
+	/* check if valid reference can be constructed at all */
+	if (engine_conf == NULL || engine_conf->engines == NULL ||
+			engine_idx >= CI_FLOW_ENGINE_MAX) {
+		return (struct ci_flow_engine_ref) {
+			.engine = NULL,
+			.engine_idx = CI_FLOW_ENGINE_MAX
+		};
+	}
+
+	return (struct ci_flow_engine_ref) {
+		.engine = engine_conf->engines->engines[engine_idx],
+		.engine_idx = engine_idx,
+	};
+}
+
+/* helper macro to iterate over list of engines as engine refs */
+#define CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf)                         \
+	for (size_t __ci_flow_engine_idx = 0;                                        \
+		__ci_flow_engine_idx < CI_FLOW_ENGINE_MAX &&                         \
+			(((engine_ref) = ci_flow_engine_ref_from_idx((engine_conf),  \
+		__ci_flow_engine_idx)).engine != NULL);                              \
+		__ci_flow_engine_idx++)
+
+/* basic checks for flow engine validity */
+static inline bool
+ci_flow_engine_is_valid(const struct ci_flow_engine *engine)
+{
+	/* is the pointer valid? */
+	if (engine == NULL)
+		return false;
+	/* does the engine have a name? */
+	if (engine->name == NULL)
+		return false;
+	/* does the engine have ops? */
+	if (engine->ops == NULL)
+		return false;
+	/* does the engine have mandatory ctx_parse op? */
+	if (engine->ops->ctx_parse == NULL)
+		return false;
+	/* flow size cannot be less than ci_flow */
+	if (engine->flow_size < sizeof(struct ci_flow))
+		return false;
+	/* alloc and free must both be defined or NULL */
+	if ((engine->ops->flow_alloc == NULL) != (engine->ops->flow_free == NULL))
+		return false;
+	/* engine looks valid */
+	return true;
+}
+
+/* helper to check whether an engine is enabled in the bitmask - thread-unsafe */
+static inline bool
+ci_flow_engine_is_enabled(const struct ci_flow_engine_conf *conf,
+		const size_t engine_idx)
+{
+	return (conf->enabled_engines & (1ULL << engine_idx)) != 0;
+}
+
+/* helper to enable an engine in the bitmask - thread-unsafe */
+static inline void
+ci_flow_engine_set_enabled(struct ci_flow_engine_conf *conf, const size_t engine_idx,
+		bool enabled)
+{
+	if (enabled)
+		conf->enabled_engines |= (1ULL << engine_idx);
+	else
+		conf->enabled_engines &= ~(1ULL << engine_idx);
+}
+
+static inline struct ci_flow *
+ci_flow_alloc(const struct ci_flow_engine_conf *engine_conf,
+		struct ci_flow_engine_ref engine_ref)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+	void *priv = engine_conf->engine_priv[engine_ref.engine_idx];
+	struct ci_flow *flow = NULL;
+	bool fallback = false;
+
+	/* if engine has an allocator callback, try it first */
+	if (engine->ops->flow_alloc != NULL)
+		flow = engine->ops->flow_alloc(engine, engine_conf->dev_data, priv);
+	/* if allocator callback is not defined or failed, use default allocator */
+	if (flow == NULL) {
+		flow = (struct ci_flow *)rte_zmalloc(NULL, engine->flow_size, 0);
+
+		/* if callback exists and we're here, allocation has fallen back */
+		if (flow != NULL && engine->ops->flow_alloc != NULL)
+			fallback = true;
+	}
+	/* set the engine data to enable correct deallocation in case of failure */
+	if (flow != NULL) {
+		/* erase the common parts - the rest is left up to the engine */
+		memset(flow, 0, sizeof(struct ci_flow));
+		flow->fallback_alloc = fallback;
+		flow->engine_idx = engine_ref.engine_idx;
+		flow->dev_data = engine_conf->dev_data;
+		flow->engine_priv = priv;
+	}
+	return flow;
+}
+
+static inline void
+ci_flow_free(struct ci_flow_engine_ref engine_ref, struct ci_flow *flow)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	if (engine->ops->flow_free != NULL && !flow->fallback_alloc)
+		engine->ops->flow_free(flow, flow->dev_data, flow->engine_priv);
+	else
+		rte_free(flow);
+}
+
+/* allocate per-device engine private data and call init - thread-unsafe */
+static inline int
+ci_flow_engine_init(struct ci_flow_engine_conf *engine_conf,
+		struct ci_flow_engine_ref engine_ref)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+	void *priv = NULL;
+	int ret;
+
+	if (engine->priv_size > 0) {
+		priv = rte_zmalloc(engine->name, engine->priv_size, 0);
+		if (priv == NULL) {
+			ret = -ENOMEM;
+			goto err;
+		}
+	}
+
+	if (engine->ops->engine_init != NULL) {
+		ret = engine->ops->engine_init(engine, engine_conf->dev_data, priv);
+		if (ret != 0)
+			goto err;
+	}
+	engine_conf->engine_priv[engine_ref.engine_idx] = priv;
+	return 0;
+err:
+	rte_free(priv);
+	return ret;
+}
+
+/* call uninit and free per-device engine private data - thread-unsafe */
+static inline void
+ci_flow_engine_uninit(struct ci_flow_engine_conf *engine_conf,
+		struct ci_flow_engine_ref engine_ref)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+	void *priv;
+
+	priv = engine_conf->engine_priv[engine_ref.engine_idx];
+
+	CI_DRV_LOG(DEBUG, "engine '%s': uninit", engine->name);
+	/* ignore uninit errors */
+	if (engine->ops->engine_uninit != NULL)
+		engine->ops->engine_uninit(engine, priv);
+
+	if (priv != NULL) {
+		rte_free(priv);
+		engine_conf->engine_priv[engine_ref.engine_idx] = NULL;
+	}
+}
+
+/* disable all engines for a specific driver instance - thread-safe */
+static inline void
+ci_flow_engine_conf_reset(struct ci_flow_engine_conf *engine_conf)
+{
+	struct ci_flow_engine_ref engine_ref;
+	struct ci_flow *flow, *tmp;
+
+	/* lock the config */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	/* free all flows - shouldn't have any at this point */
+	RTE_TAILQ_FOREACH_SAFE(flow, &engine_conf->flows, node, tmp) {
+		TAILQ_REMOVE(&engine_conf->flows, flow, node);
+		/* can't make it into the list if this was invalid, so no checks */
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf, flow->engine_idx);
+		ci_flow_free(engine_ref, flow);
+	}
+
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		if (!ci_flow_engine_is_enabled(engine_conf, engine_ref.engine_idx))
+			continue;
+		/* ignore errors */
+		ci_flow_engine_uninit(engine_conf, engine_ref);
+		ci_flow_engine_set_enabled(engine_conf,
+				engine_ref.engine_idx, false);
+	}
+
+	/* erase device pointer */
+	engine_conf->dev_data = NULL;
+	engine_conf->engines = NULL;
+
+	/* unlock the config */
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+}
+
+/* enable all engines for a specific driver instance - thread-unsafe */
+static inline int
+ci_flow_engine_conf_init(struct ci_flow_engine_conf *engine_conf,
+		const struct ci_flow_engine_list *engine_list,
+		struct rte_eth_dev_data *dev_data)
+{
+	struct ci_flow_engine_ref engine_ref;
+
+	/* reject invalid configuration */
+	if (engine_conf == NULL || engine_list == NULL || dev_data == NULL)
+		return -1;
+
+	/* init the lock */
+	rte_rwlock_init(&engine_conf->config_lock);
+
+	/* store data in conf */
+	engine_conf->dev_data = dev_data;
+	engine_conf->engines = engine_list;
+
+	/* init the flow list */
+	TAILQ_INIT(&engine_conf->flows);
+
+	/* enable all engines */
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		/* skip invalid engines */
+		if (!ci_flow_engine_is_valid(engine_ref.engine)) {
+			CI_DRV_LOG(DEBUG, "engine[%zu]: invalid, skipping",
+					engine_ref.engine_idx);
+			continue;
+		}
+		if (ci_flow_engine_init(engine_conf, engine_ref) != 0) {
+			CI_DRV_LOG(DEBUG, "engine '%s': init failed, skipping",
+					engine_ref.engine->name);
+			continue;
+		}
+
+		ci_flow_engine_set_enabled(engine_conf,
+				engine_ref.engine_idx, true);
+		CI_DRV_LOG(DEBUG, "engine '%s': enabled", engine_ref.engine->name);
+	}
+	return 0;
+}
+
+/* validate whether a flow is valid for a specific engine configuration - thread-unsafe */
+static inline bool
+ci_flow_is_valid(const struct ci_flow *flow,
+		const struct ci_flow_engine_conf *engine_conf)
+{
+	/* is the pointer valid? */
+	if (flow == NULL)
+		return false;
+	/* is the conf initialized? */
+	if (engine_conf == NULL || engine_conf->dev_data == NULL ||
+			engine_conf->engines == NULL)
+		return false;
+	/* does the flow belong to this device? */
+	if (flow->dev_data != engine_conf->dev_data)
+		return false;
+	/* can we find the engine that created this flow? */
+	if (flow->engine_idx >= CI_FLOW_ENGINE_MAX)
+		return false;
+	/* engine must be enabled */
+	if (!ci_flow_engine_is_enabled(engine_conf, flow->engine_idx))
+		return false;
+	/* flow looks valid */
+	return true;
+}
+
+/* default empty pattern graph definitions */
+enum ci_flow_empty_graph_node_id {
+	CI_FLOW_EMPTY_GRAPH_NODE_START = RTE_FLOW_NODE_FIRST,
+	CI_FLOW_EMPTY_GRAPH_NODE_ANY,
+	CI_FLOW_EMPTY_GRAPH_NODE_END,
+};
+
+static const struct rte_flow_graph ci_flow_empty_graph = {
+	.nodes = (struct rte_flow_graph_node []) {
+		[CI_FLOW_EMPTY_GRAPH_NODE_START] = {
+			.name = "START",
+		},
+		[CI_FLOW_EMPTY_GRAPH_NODE_ANY] = {
+			.name = "ANY",
+			.type = RTE_FLOW_ITEM_TYPE_ANY,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY,
+		},
+		[CI_FLOW_EMPTY_GRAPH_NODE_END] = {
+			.name = "END",
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	.edges = (struct rte_flow_graph_edge []) {
+		[CI_FLOW_EMPTY_GRAPH_NODE_START] = {
+			.next = (const size_t []) {
+				CI_FLOW_EMPTY_GRAPH_NODE_ANY,
+				CI_FLOW_EMPTY_GRAPH_NODE_END,
+				RTE_FLOW_NODE_EDGE_END,
+			},
+		},
+		[CI_FLOW_EMPTY_GRAPH_NODE_ANY] = {
+			.next = (const size_t []) {
+				CI_FLOW_EMPTY_GRAPH_NODE_END,
+				RTE_FLOW_NODE_EDGE_END,
+			},
+		},
+	}
+};
+
+/* concrete pattern-matching mode selected from graph/callback presence */
+enum ci_match_type {
+	CI_MATCH_EMPTY,    /* no graph, no callback */
+	CI_MATCH_CALLBACK, /* callback only */
+	CI_MATCH_GRAPH,    /* graph only */
+	CI_MATCH_ALL,      /* callback + graph */
+};
+
+/*
+ * helper to match a pattern against an engine's pattern graph and/or callback,
+ * depending on the match type. Thread-unsafe.
+ */
+static inline int
+ci_flow_match(const struct ci_flow_engine *engine,
+		const struct rte_flow_item pattern[],
+		struct ci_flow_engine_ctx *ctx,
+		enum ci_match_type match_type,
+		struct rte_flow_error *error)
+{
+	switch (match_type) {
+	case CI_MATCH_EMPTY:
+		/* for empty matching, NULL pattern is not an error */
+		if (pattern != NULL) {
+			return rte_flow_graph_parse(&ci_flow_empty_graph,
+					pattern, error, ctx);
+		}
+		return 0;
+
+	case CI_MATCH_CALLBACK:
+		/* for callback matching, pattern cannot be NULL */
+		if (pattern == NULL) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+					"Pattern cannot be NULL");
+		}
+		return engine->ops->pattern_parse(ctx, pattern, error);
+
+	case CI_MATCH_GRAPH:
+		/* for graph matching, pattern cannot be NULL */
+		if (pattern == NULL) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+					"Pattern cannot be NULL");
+		}
+		return rte_flow_graph_parse(engine->graph, pattern, error, ctx);
+
+	case CI_MATCH_ALL:
+	{
+		int ret;
+
+		/* for callback + graph matching, pattern cannot be NULL */
+		if (pattern == NULL) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+					"Pattern cannot be NULL");
+		}
+		ret = engine->ops->pattern_parse(ctx, pattern, error);
+		if (ret != 0)
+			return ret;
+		return rte_flow_graph_parse(engine->graph, pattern, error, ctx);
+	}
+
+	default:
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+				"Invalid match type");
+	}
+}
+
+/* parse a flow using a specific engine - thread-unsafe */
+static inline int
+ci_flow_parse(const struct ci_flow_engine_conf *engine_conf,
+		const struct ci_flow_engine *engine,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	enum ci_match_type match_type;
+	struct ci_flow_engine_ctx *ctx;
+	int ret = 0;
+
+	/*
+	 * Determine the type of matching we are going to perform based on the
+	 * presence of pattern graph and pattern_parse callback. The logic is as
+	 * follows:
+	 *
+	 * - if graph but no callback, match against graph
+	 *
+	 * Expected default case: pattern matching is graph based, no special
+	 * handling for any pattern items.
+	 *
+	 * - if both graph and callback, match against callback + graph
+	 *
+	 * Preprocessor case, i.e. preprocess the pattern with the callback
+	 * before handling the matching to the graph engine. The assumption is
+	 * that the graph will be set up with a proper ignore list to skip over
+	 * nodes that weren't meant for the graph processing.
+	 *
+	 * - if no graph but callback, match against callback
+	 *
+	 * Fully custom pattern parsing case.
+	 *
+	 * - if no graph and no callback, match against empty graph
+	 *
+	 * "Pattern is not meaningful" case, for engines that do not care about
+	 * the pattern at all. A default matching behavior against empty
+	 * patterns is provided (i.e. allow NULL pattern, and allow END or ANY
+	 * -> END patterns). Note that this is not the same as ignoring pattern
+	 * entirely: the engine will still reject patterns that are not empty.
+	 */
+	match_type = engine->graph == NULL ?
+		(engine->ops->pattern_parse == NULL ? CI_MATCH_EMPTY : CI_MATCH_CALLBACK) :
+		(engine->ops->pattern_parse == NULL ? CI_MATCH_GRAPH : CI_MATCH_ALL);
+
+	CI_DRV_LOG(DEBUG, "engine '%s': parsing flow", engine->name);
+
+	/* allocate context */
+	ctx = (struct ci_flow_engine_ctx *)calloc(1,
+			RTE_MAX(engine->ctx_size, sizeof(struct ci_flow_engine_ctx)));
+	if (ctx == NULL) {
+		return rte_flow_error_set(error, ENOMEM,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Failed to allocate memory for rule engine context");
+	}
+	ctx->dev_data = engine_conf->dev_data;
+	flow->dev_data = engine_conf->dev_data;
+
+	/* parse flow parameters */
+	ret = engine->ops->ctx_parse(actions, attr, ctx, error);
+
+	/* context init failed - that means engine can't be used for this flow */
+	if (ret != 0)
+		goto free_ctx;
+
+	/* match the pattern */
+	ret = ci_flow_match(engine, pattern, ctx, match_type, error);
+
+	/* check if pattern didn't match */
+	if (ret != 0)
+		goto free_ctx;
+
+	/* final verification, if the operation is defined */
+	if (engine->ops->ctx_validate != NULL)
+		ret = engine->ops->ctx_validate(ctx, error);
+
+	/* finalization failed - mismatch between parsed data and context data */
+	if (ret != 0)
+		goto free_ctx;
+
+	/* if we need to build rules from context, do it */
+	if (engine->ops->ctx_to_flow != NULL) {
+		ret = engine->ops->ctx_to_flow(ctx, flow, error);
+
+		/* flow building failed - something wrong with context data */
+		if (ret != 0)
+			goto free_ctx;
+	}
+	/* success */
+	ret = 0;
+
+free_ctx:
+	free(ctx);
+	return ret;
+}
+
+/* install a flow using its appropriate engine - thread-unsafe */
+static inline int
+ci_flow_install(struct ci_flow_engine_ref engine_ref,
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	if (engine->ops->flow_install != NULL)
+		return engine->ops->flow_install(flow, error);
+
+	return 0;
+}
+
+/* uninstall a flow using its appropriate engine - thread-unsafe */
+static inline int
+ci_flow_uninstall(struct ci_flow_engine_ref engine_ref,
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	/* uninstall the flow if required */
+	if (engine->ops->flow_uninstall != NULL)
+		return engine->ops->flow_uninstall(flow, error);
+
+	return 0;
+}
+
+/*
+ * The following functions are designed to be called from the context of
+ * rte_flow API implementations and are provided as default implementations.
+ *
+ * Thread-safe.
+ */
+
+/* default implementation of rte_flow_create using flow engines */
+static inline struct rte_flow *
+ci_flow_create(struct ci_flow_engine_conf *engine_conf,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error)
+{
+	struct ci_flow_engine_ref engine_ref;
+	struct ci_flow *flow = NULL;
+	int ret;
+
+	if (attr == NULL || actions == NULL) {
+		CI_DRV_LOG(DEBUG, "attr or actions is NULL");
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR, NULL,
+				"Attributes and actions cannot be NULL");
+		return NULL;
+	}
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	/* find an engine that can handle this flow */
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		if (!ci_flow_engine_is_enabled(engine_conf, engine_ref.engine_idx))
+			continue;
+
+		flow = ci_flow_alloc(engine_conf, engine_ref);
+		if (flow == NULL) {
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to allocate flow",
+					engine_ref.engine->name);
+			rte_flow_error_set(error, ENOMEM,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to allocate memory for flow rule");
+			/* this is a serious error so don't continue */
+			goto unlock;
+		}
+
+		ret = ci_flow_parse(engine_conf, engine_ref.engine, attr, pattern,
+				actions, flow, error);
+
+		/* parsing failed - free the flow and try next engine */
+		if (ret != 0) {
+			ci_flow_free(engine_ref, flow);
+			if (error != NULL)
+				CI_DRV_LOG(DEBUG, "engine '%s' rejected flow: %s",
+						engine_ref.engine->name, error->message);
+			else
+				CI_DRV_LOG(DEBUG, "engine '%s' rejected flow",
+						engine_ref.engine->name);
+			continue;
+		}
+
+		CI_DRV_LOG(DEBUG, "engine '%s' accepted flow, installing",
+				engine_ref.engine->name);
+
+		/* engine accepted the flow - install is now a hard commitment */
+		ret = ci_flow_install(engine_ref, flow, error);
+		if (ret != 0) {
+			/* install failed after parse accepted - this is an
+			 * engine bug, do not try other engines
+			 */
+			if (error != NULL)
+				CI_DRV_LOG(DEBUG, "engine '%s' install failed: %s",
+						engine_ref.engine->name, error->message);
+			else
+				CI_DRV_LOG(DEBUG, "engine '%s' install failed",
+						engine_ref.engine->name);
+			ci_flow_free(engine_ref, flow);
+			flow = NULL;
+			goto unlock;
+		}
+
+		CI_DRV_LOG(DEBUG, "flow installed by engine '%s'",
+				engine_ref.engine->name);
+		/* success */
+		TAILQ_INSERT_TAIL(&engine_conf->flows, flow, node);
+		goto unlock;
+	}
+
+	/* no engine could handle this flow */
+	CI_DRV_LOG(DEBUG, "no engine accepted the flow");
+	flow = NULL;
+	rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"No flow engine could handle the requested flow");
+unlock:
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return (struct rte_flow *)flow;
+}
+
+/* default implementation of rte_flow_validate using flow engines */
+static inline int
+ci_flow_validate(struct ci_flow_engine_conf *engine_conf,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error)
+{
+	struct ci_flow_engine_ref engine_ref;
+	int ret;
+
+	if (attr == NULL || actions == NULL) {
+		CI_DRV_LOG(DEBUG, "attr or actions is NULL");
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR, NULL,
+				"Attributes and actions cannot be NULL");
+	}
+
+	/* lock the config for reading */
+	rte_rwlock_read_lock(&engine_conf->config_lock);
+
+	/* find an engine that can handle this flow */
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		struct ci_flow *flow;
+
+		if (!ci_flow_engine_is_enabled(engine_conf, engine_ref.engine_idx))
+			continue;
+
+		/* use OS allocator as we're not keeping the flow */
+		flow = (struct ci_flow *)calloc(1, engine_ref.engine->flow_size);
+		if (flow == NULL) {
+			/* this is a serious error so don't continue */
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to allocate flow",
+					engine_ref.engine->name);
+			ret = rte_flow_error_set(error, ENOMEM,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to allocate memory for flow rule");
+			goto unlock;
+		}
+		/* set up the flow fields */
+		flow->fallback_alloc = false;
+		flow->engine_idx = engine_ref.engine_idx;
+		flow->dev_data = engine_conf->dev_data;
+		flow->engine_priv = engine_conf->engine_priv[engine_ref.engine_idx];
+
+		/* try to parse the flow with this engine */
+		ret = ci_flow_parse(engine_conf, engine_ref.engine, attr, pattern,
+				actions, flow, error);
+		free(flow);
+
+		if (ret == 0) {
+			CI_DRV_LOG(DEBUG, "engine '%s' accepted flow",
+					engine_ref.engine->name);
+			goto unlock;
+		} else if (error != NULL) {
+			CI_DRV_LOG(DEBUG, "engine '%s' rejected flow: %s",
+					engine_ref.engine->name, error->message);
+		} else {
+			CI_DRV_LOG(DEBUG, "engine '%s' rejected flow",
+					engine_ref.engine->name);
+		}
+	}
+	/* no engine could handle this flow */
+	CI_DRV_LOG(DEBUG, "no engine accepted the flow");
+	ret = rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"No flow engine could handle the requested flow");
+unlock:
+	rte_rwlock_read_unlock(&engine_conf->config_lock);
+	return ret;
+}
+
+/* default implementation of rte_flow_destroy using flow engines. */
+static inline int
+ci_flow_destroy(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow *rte_flow,
+		struct rte_flow_error *error)
+{
+	struct ci_flow *flow = (struct ci_flow *)rte_flow;
+	struct ci_flow_engine_ref engine_ref;
+	int ret = 0;
+
+	if (rte_flow == NULL) {
+		CI_DRV_LOG(DEBUG, "flow handle is NULL");
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Flow handle cannot be NULL");
+	}
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	/* validate the flow */
+	if (!ci_flow_is_valid(flow, engine_conf)) {
+		CI_DRV_LOG(DEBUG, "invalid flow handle");
+		ret = rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Invalid flow handle");
+		goto unlock;
+	}
+	engine_ref = ci_flow_engine_ref_from_idx(engine_conf, flow->engine_idx);
+	CI_DRV_LOG(DEBUG, "uninstalling flow (engine '%s')",
+			engine_ref.engine->name);
+
+	ret = ci_flow_uninstall(engine_ref, flow, error);
+
+	if (ret != 0) {
+		if (error != NULL)
+			CI_DRV_LOG(DEBUG, "uninstall failed: %s", error->message);
+		else
+			CI_DRV_LOG(DEBUG, "uninstall failed");
+		goto unlock;
+	}
+
+	/* remove the flow from the list and free it */
+	TAILQ_REMOVE(&engine_conf->flows, flow, node);
+	ci_flow_free(engine_ref, flow);
+unlock:
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return ret;
+}
+
+/* default implementation of rte_flow_flush using flow engines */
+static inline int
+ci_flow_flush(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow_error *error)
+{
+	struct ci_flow *flow, *tmp;
+
+	CI_DRV_LOG(DEBUG, "removing all flows");
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	/* iterate over all flows and uninstall them */
+	RTE_TAILQ_FOREACH_SAFE(flow, &engine_conf->flows, node, tmp) {
+		struct ci_flow_engine_ref engine_ref;
+
+		/* this shouldn't happen */
+		if (!ci_flow_is_valid(flow, engine_conf))
+			continue;
+
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf,
+				flow->engine_idx);
+
+		/* ignore failures */
+		ci_flow_uninstall(engine_ref, flow, error);
+
+		TAILQ_REMOVE(&engine_conf->flows, flow, node);
+		ci_flow_free(engine_ref, flow);
+	}
+
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return 0;
+}
+
+/* default implementation of rte_flow_query using flow engines */
+static inline int
+ci_flow_query(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow *rte_flow,
+		const struct rte_flow_action *action,
+		void *data,
+		struct rte_flow_error *error)
+{
+	struct ci_flow *flow = (struct ci_flow *)rte_flow;
+	struct ci_flow_engine_ref engine_ref;
+	int ret;
+
+	if (action == NULL || data == NULL) {
+		CI_DRV_LOG(DEBUG, "action or data is NULL");
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Action or data cannot be NULL");
+	}
+
+	/* lock the config for reading */
+	rte_rwlock_read_lock(&engine_conf->config_lock);
+
+	/* validate the flow first */
+	if (!ci_flow_is_valid(flow, engine_conf)) {
+		CI_DRV_LOG(DEBUG, "invalid flow handle");
+		ret = rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Invalid flow handle");
+		goto unlock;
+	}
+	/* find the engine that created this flow */
+	engine_ref = ci_flow_engine_ref_from_idx(engine_conf, flow->engine_idx);
+	/* query the flow if supported */
+	if (engine_ref.engine->ops->flow_query != NULL) {
+		ret = engine_ref.engine->ops->flow_query(flow, action, data, error);
+
+		if (ret != 0 && error != NULL)
+			CI_DRV_LOG(DEBUG, "engine '%s' query failed: %s",
+					engine_ref.engine->name, error->message);
+		else if (ret != 0)
+			CI_DRV_LOG(DEBUG, "engine '%s' query failed",
+					engine_ref.engine->name);
+	} else {
+		CI_DRV_LOG(DEBUG, "engine '%s' does not support querying",
+				engine_ref.engine->name);
+		ret = rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"Flow engine does not support querying");
+	}
+unlock:
+	rte_rwlock_read_unlock(&engine_conf->config_lock);
+
+	return ret;
+}
+
+/* dump flow rule chunks using memdump - helper for ci_flow_dump */
+#define CI_FLOW_DUMP_CHUNK_BYTES 32
+
+static inline void
+ci_flow_dump_one(FILE *file, const char *driver, const char *engine,
+		const void *data, size_t data_len)
+{
+	const uint8_t *raw = (const uint8_t *)data;
+	const size_t nchunks =
+		(data_len + CI_FLOW_DUMP_CHUNK_BYTES - 1) /
+		CI_FLOW_DUMP_CHUNK_BYTES;
+	char title[64];
+	size_t ci;
+
+	fprintf(file, "FLOW DUMP: driver=%s engine=%s\n", driver, engine);
+	fprintf(file, "FLOW DUMP: DATA size=%zu chunks=%zu chunk_bytes=%d\n",
+		data_len, nchunks, CI_FLOW_DUMP_CHUNK_BYTES);
+
+	for (ci = 0; ci < nchunks; ci++) {
+		const size_t off = ci * CI_FLOW_DUMP_CHUNK_BYTES;
+		const size_t clen =
+			RTE_MIN((size_t)CI_FLOW_DUMP_CHUNK_BYTES,
+				data_len - off);
+		snprintf(title, sizeof(title), "FLOW DUMP: chunk %03zu/%03zu",
+			 ci + 1, nchunks);
+		rte_memdump(file, title, raw + off, clen);
+	}
+}
+
+/* default implementation of rte_flow_dev_dump using flow engines */
+static inline int
+ci_flow_dump(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow *flow,
+		FILE *file,
+		struct rte_flow_error *error)
+{
+	struct ci_flow_engine_ref engine_ref;
+	struct ci_flow *cur;
+	bool found = false;
+	const char *driver_name =
+#ifdef RTE_COMPONENT_NAME
+			RTE_STR(RTE_COMPONENT_NAME);
+#else
+			"unknown";
+#endif
+
+	rte_rwlock_read_lock(&engine_conf->config_lock);
+
+	TAILQ_FOREACH(cur, &engine_conf->flows, node) {
+		const void *data;
+		size_t data_len;
+
+		if (flow != NULL && (struct rte_flow *)cur != flow)
+			continue;
+
+		/* is this flow valid? */
+		if (!ci_flow_is_valid(cur, engine_conf)) {
+			CI_DRV_LOG(DEBUG, "invalid flow handle: %p, skipping", cur);
+			continue;
+		}
+
+		found = true;
+
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf, cur->engine_idx);
+
+		data = RTE_PTR_ADD(cur, sizeof(struct ci_flow));
+		data_len = engine_ref.engine->flow_size - sizeof(struct ci_flow);
+
+		/*
+		 * when data_len is 0 the dump loop would not access the data
+		 * pointer, but static analysis tools may flag this as a
+		 * potential NULL dereference, so skip dump when data_len is 0.
+		 */
+		if (data_len == 0) {
+			CI_DRV_LOG(DEBUG, "flow data length is 0, skipping dump");
+			continue;
+		}
+
+		ci_flow_dump_one(file, driver_name, engine_ref.engine->name,
+				data, data_len);
+	}
+
+	rte_rwlock_read_unlock(&engine_conf->config_lock);
+
+	if (flow != NULL && !found) {
+		return rte_flow_error_set(error, ENOENT,
+			RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+			"Flow not found");
+	}
+
+	return 0;
+}
+
+#endif /* _COMMON_INTEL_FLOW_ENGINE_H_ */
Signature · 1 line
2.52.0
#11 lore raw

PATCH 3/21 net/intel/common: add utility functions

In reply to Anatoly Burakov

A lot of parsers will rely on doing the same things over and over, so create a header with utility functions to aid in writing parsers.

---
 drivers/net/intel/common/flow_util.h | 183 +++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)
 create mode 100644 drivers/net/intel/common/flow_util.h
drivers/net/intel/common/flow_util.h new file +183 -0
new file mode 100644
index 0000000000..57f12c4637
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _COMMON_INTEL_FLOW_UTIL_H_
+#define _COMMON_INTEL_FLOW_UTIL_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * Utility functions primarily intended for flow parsers.
+ */
+
+/**
+ * Check if memory region is filled with a specific byte value.
+ *
+ * @param ptr
+ *   Pointer to memory region.
+ * @param len
+ *   Length in bytes.
+ * @param val
+ *   Byte value to check (e.g. 0x00 or 0xFF).
+ * @return
+ *   true if all bytes equal val, false otherwise.
+ */
+static inline bool
+ci_is_all_byte(const void *ptr, size_t len, uint8_t val)
+{
+	const uint8_t *bytes = (const uint8_t *)ptr;
+	const uint32_t pattern32 = 0x01010101U * val;
+	size_t i = 0;
+
+	/* Process 4-byte chunks using memcpy */
+	for (; i + 4 <= len; i += 4) {
+		uint32_t chunk;
+		memcpy(&chunk, bytes + i, 4);
+		if (chunk != pattern32)
+			return false;
+	}
+
+	/* Process remaining bytes */
+	for (; i < len; i++) {
+		if (bytes[i] != val)
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * Check if bytes are all 0x00 OR all 0xFF.
+ *
+ * @param ptr
+ *   Pointer to memory region.
+ * @param len
+ *   Length in bytes.
+ * @return
+ *   true if all bytes are 0x00 OR all bytes are 0xFF, false otherwise.
+ */
+static inline bool
+ci_is_all_zero_or_masked(const void *ptr, size_t len)
+{
+	const uint8_t *bytes = (const uint8_t *)ptr;
+	uint8_t first_val;
+
+	/* zero length cannot be valid */
+	if (len == 0)
+		return false;
+
+	first_val = bytes[0];
+
+	if (first_val != 0x00 && first_val != 0xFF)
+		return false;
+
+	return ci_is_all_byte(ptr, len, first_val);
+}
+
+/**
+ * Check if a value has no bits outside the mask, and within the mask is
+ * either all-zero or all-one.
+ *
+ * This is intended for bitfields e.g. VLAN_TCI. For byte-aligned fields,
+ * use CI_FIELD_IS_ZERO_OR_MASKED below.
+ *
+ * @param value
+ *   Data value to check.
+ * @param mask
+ *   Mask to compare against.
+ * @return
+ *   true if (value & ~mask) == 0 AND (value & mask) is 0 or mask,
+ *   false otherwise.
+ */
+static inline bool
+ci_is_zero_or_masked(uint64_t value, uint64_t mask)
+{
+	uint64_t masked = value & mask;
+	uint64_t unmasked = value & ~mask;
+
+	return unmasked == 0 && (masked == 0 || masked == mask);
+}
+
+/**
+ * Check if a struct field is fully masked or unmasked.
+ *
+ * @param field_ptr
+ *   Pointer to the mask field (e.g. &eth_mask->hdr.src_addr).
+ */
+#define CI_FIELD_IS_ZERO_OR_MASKED(field_ptr) \
+	ci_is_all_zero_or_masked((field_ptr), sizeof(*(field_ptr)))
+
+/**
+ * Check if a struct field is all 0x00.
+ *
+ * @param field_ptr
+ *   Pointer to the mask field (e.g. &eth_mask->hdr.src_addr).
+ */
+#define CI_FIELD_IS_ZERO(field_ptr) \
+	ci_is_all_byte((field_ptr), sizeof(*(field_ptr)), 0x00)
+
+/**
+ * Check if a struct field is all 0xFF.
+ *
+ * @param field_ptr
+ *   Pointer to the mask field (e.g. &eth_mask->hdr.src_addr).
+ */
+#define CI_FIELD_IS_MASKED(field_ptr) \
+	ci_is_all_byte((field_ptr), sizeof(*(field_ptr)), 0xFF)
+
+/**
+ * Convert 24-bit big-endian value to host byte order.
+ *
+ * Used to extract 24-bit big-endian values (e.g. VXLAN VNI).
+ *
+ * @param val
+ *   Pointer to 3-byte big-endian value.
+ * @return
+ *   Value in host byte order.
+ */
+static inline uint32_t
+ci_be24_to_cpu(const uint8_t val[3])
+{
+	return (val[0] << 16) | (val[1] << 8) | val[2];
+}
+
+/**
+ * Check if a character is a valid hexadecimal digit.
+ *
+ * @param c
+ *   Character to check.
+ * @return
+ *   true if c is in [0-9a-fA-F], false otherwise.
+ */
+static inline bool
+ci_is_hex_char(unsigned char c)
+{
+	return ((c >= '0' && c <= '9') ||
+		(c >= 'a' && c <= 'f') ||
+		(c >= 'A' && c <= 'F'));
+}
+
+/**
+ * Convert hex character to 4-bit value.
+ *
+ * @param c
+ *   Hex character ('0'-'9', 'a'-'f', 'A'-'F').
+ * @return
+ *   Value 0-15, or 0 if invalid.
+ */
+static inline unsigned char
+ci_hex_char_to_nibble(unsigned char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	return 0;
+}
+
+#endif /* _INTEL_COMMON_FLOW_UTIL_H_ */
Signature · 1 line
2.52.0
#12 lore raw

PATCH 4/21 net/ixgbe: add support for common flow parsing

In reply to Anatoly Burakov

Implement support for common flow parsing infrastructure in preparation for migration of flow engines.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c | 15 ++++++-
 drivers/net/intel/ixgbe/ixgbe_ethdev.h |  5 +++
 drivers/net/intel/ixgbe/ixgbe_flow.c   | 55 +++++++++++++++++++++++++-
 drivers/net/intel/ixgbe/ixgbe_flow.h   | 12 ++++++
 4 files changed, 84 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow.h
drivers/net/intel/ixgbe/ixgbe_ethdev.c +13 -2
index c5010f623c..ebc4fab704 100644
@@ -46,6 +46,7 @@
 #include "base/ixgbe_phy.h"
 #include "base/ixgbe_osdep.h"
 #include "ixgbe_regs.h"
+#include "ixgbe_flow.h"
 
 /*
  * High threshold controlling when to start sending XOFF frames. Must be at
@@ -1343,6 +1344,10 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 	/* initialize Traffic Manager configuration */
 	ixgbe_tm_conf_init(eth_dev);
 
+	/* initialize flow engine configuration */
+	ci_flow_engine_conf_init(&ad->flow_engine_conf,
+			&ixgbe_flow_engine_list, eth_dev->data);
+
 	return 0;
 
 err_l2_tn_filter_init:
@@ -3089,8 +3094,8 @@ ixgbe_dev_set_link_down(struct rte_eth_dev *dev)
 static int
 ixgbe_dev_close(struct rte_eth_dev *dev)
 {
-	struct ixgbe_hw *hw =
-		IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *ad = dev->data->dev_private;
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(ad);
 	struct rte_pci_device *pci_dev = RTE_CLASS_TO_BUS_DEVICE(dev, *pci_dev);
 	struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
 	int retries = 0;
@@ -3100,6 +3105,9 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
 		return 0;
 
+	/* uninstall all flows */
+	ci_flow_flush(&ad->flow_engine_conf, NULL);
+
 	ixgbe_pf_reset_hw(hw);
 
 	ret = ixgbe_dev_stop(dev);
@@ -3154,6 +3162,9 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	rte_free(dev->security_ctx);
 	dev->security_ctx = NULL;
 
+	/* reset flow engines */
+	ci_flow_engine_conf_reset(&ad->flow_engine_conf);
+
 	return ret;
 }
 
drivers/net/intel/ixgbe/ixgbe_ethdev.h +5 -0
index 5d3243cb4d..cde0ee8fda 100644
@@ -22,6 +22,8 @@
 #include <bus_pci_driver.h>
 #include <rte_tm_driver.h>
 
+#include "../common/flow_engine.h"
+
 /* need update link, bit flag */
 #define IXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
 #define IXGBE_FLAG_MAILBOX          (uint32_t)(1 << 1)
@@ -346,6 +348,7 @@ struct ixgbe_l2_tn_info {
 };
 
 struct rte_flow {
+	struct ci_flow flow;
 	enum rte_filter_type filter_type;
 	/* security flows are not rte_filter_type */
 	bool is_security;
@@ -492,6 +495,8 @@ struct ixgbe_adapter {
 	struct rte_timecounter      tx_tstamp_tc;
  	struct ixgbe_tm_conf        tm_conf;
 
+	struct ci_flow_engine_conf flow_engine_conf;
+
 	/* For RSS reta table update */
 	uint8_t rss_reta_updated;
 
drivers/net/intel/ixgbe/ixgbe_flow.c +54 -1

111 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h new file +12 -0
new file mode 100644
index 0000000000..5e68c9886c
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef _IXGBE_FLOW_H_
+#define _IXGBE_FLOW_H_
+
+#include "../common/flow_engine.h"
+
+extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
+
+#endif /*  _IXGBE_FLOW_H_ */
Signature · 1 line
2.52.0
#13 lore raw

PATCH 5/21 net/ixgbe: reimplement ethertype parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for ethertype.

The old ethertype parser was accepting certain things that were later rejected by the actual ethertype installation code, in particular DROP action as well as dst MAC address filtering. This was removed from the graph parser.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c        |  19 --
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 222 +---------------
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   8 +
 .../net/intel/ixgbe/ixgbe_flow_ethertype.c    | 245 ++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build           |   1 +
 5 files changed, 260 insertions(+), 235 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ethertype.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +0 -19
index ebc4fab704..5465447bdb 100644
@@ -6855,25 +6855,6 @@ ixgbe_add_del_ethertype_filter(struct ixgbe_adapter *adapter,
 	int ret;
 	struct ixgbe_ethertype_filter ethertype_filter;
 
-	if (filter->queue >= IXGBE_MAX_RX_QUEUE_NUM)
-		return -EINVAL;
-
-	if (filter->ether_type == RTE_ETHER_TYPE_IPV4 ||
-		filter->ether_type == RTE_ETHER_TYPE_IPV6) {
-		PMD_DRV_LOG(ERR, "unsupported ether_type(0x%04x) in"
-			" ethertype filter.", filter->ether_type);
-		return -EINVAL;
-	}
-
-	if (filter->flags & RTE_ETHTYPE_FLAGS_MAC) {
-		PMD_DRV_LOG(ERR, "mac compare is unsupported.");
-		return -EINVAL;
-	}
-	if (filter->flags & RTE_ETHTYPE_FLAGS_DROP) {
-		PMD_DRV_LOG(ERR, "drop option is unsupported.");
-		return -EINVAL;
-	}
-
 	ret = ixgbe_ethertype_filter_lookup(filter_info, filter->ether_type);
 	if (ret >= 0 && add) {
 		PMD_DRV_LOG(ERR, "ethertype (0x%04x) filter exists.",
drivers/net/intel/ixgbe/ixgbe_flow.c +6 -216

292 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +8 -0
index 5e68c9886c..d7694283a5 100644
@@ -5,8 +5,16 @@
 #ifndef _IXGBE_FLOW_H_
 #define _IXGBE_FLOW_H_
 
+#include "../common/flow_check.h"
 #include "../common/flow_engine.h"
 
+int
+ixgbe_flow_actions_check(const struct ci_flow_actions *actions,
+		const struct ci_flow_actions_check_param *param,
+		struct rte_flow_error *error);
+
 extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 
+extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
+
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_ethertype.c new file +245 -0

245 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 7e737ee7b4..54d7e87de8 100644
@@ -11,6 +11,7 @@ sources += files(
         'ixgbe_ethdev.c',
         'ixgbe_fdir.c',
         'ixgbe_flow.c',
+        'ixgbe_flow_ethertype.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#14 lore raw

PATCH 6/21 net/ixgbe: reimplement syn parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for SYN.

As a result of this migration, queue index validation has changed:

- queue is now validated at parse time against the configured number of Rx queues (nb_rx_queues), rather than at install time against the hardware maximum (IXGBE_MAX_RX_QUEUE_NUM)
- the per-function queue bound check in ixgbe_syn_filter_set() has been removed as it is no longer needed

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c   |   3 -
 drivers/net/intel/ixgbe/ixgbe_flow.c     | 250 +-------------------
 drivers/net/intel/ixgbe/ixgbe_flow.h     |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_syn.c | 283 +++++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build      |   1 +
 5 files changed, 287 insertions(+), 251 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_syn.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +0 -3
index 5465447bdb..e8736be337 100644
@@ -6475,9 +6475,6 @@ ixgbe_syn_filter_set(struct ixgbe_adapter *adapter,
 	uint32_t syn_info;
 	uint32_t synqf;
 
-	if (filter->queue >= IXGBE_MAX_RX_QUEUE_NUM)
-		return -EINVAL;
-
 	syn_info = filter_info->syn_info;
 
 	if (add) {
drivers/net/intel/ixgbe/ixgbe_flow.c +2 -248

313 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index d7694283a5..453a23d3b6 100644
@@ -16,5 +16,6 @@ ixgbe_flow_actions_check(const struct ci_flow_actions *actions,
 extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 
 extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
+extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_syn.c new file +283 -0

283 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 54d7e87de8..bd9be0add3 100644
@@ -12,6 +12,7 @@ sources += files(
         'ixgbe_fdir.c',
         'ixgbe_flow.c',
         'ixgbe_flow_ethertype.c',
+        'ixgbe_flow_syn.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#15 lore raw

PATCH 7/21 net/ixgbe: reimplement L2 tunnel parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for L2 tunnel.

---
 drivers/net/intel/ixgbe/ixgbe_flow.c       | 202 +-----------------
 drivers/net/intel/ixgbe/ixgbe_flow.h       |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c | 230 +++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build        |   1 +
 4 files changed, 233 insertions(+), 201 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c
drivers/net/intel/ixgbe/ixgbe_flow.c +1 -201
index bb11ce72a5..ddcc7aa83c 100644
@@ -69,11 +69,6 @@ struct ixgbe_fdir_rule_ele {
 	struct ixgbe_filter_ele_base base;
 	struct ixgbe_fdir_rule filter_info;
 };
-/* l2_tunnel filter list structure */
-struct ixgbe_eth_l2_tunnel_conf_ele {
-	struct ixgbe_filter_ele_base base;
-	struct ixgbe_l2_tunnel_conf filter_info;
-};
 /* rss filter list structure */
 struct ixgbe_rss_conf_ele {
 	struct ixgbe_filter_ele_base base;
@@ -89,6 +84,7 @@ const struct ci_flow_engine_list ixgbe_flow_engine_list = {
 	{
 		&ixgbe_ethertype_flow_engine,
 		&ixgbe_syn_flow_engine,
+		&ixgbe_l2_tunnel_flow_engine,
 	},
 };
 
@@ -663,161 +659,6 @@ ixgbe_parse_ntuple_filter(struct rte_eth_dev *dev,
 	return 0;
 }
 
-/**
- * Parse the rule to see if it is a L2 tunnel rule.
- * And get the L2 tunnel filter info BTW.
- * Only support E-tag now.
- * pattern:
- * The first not void item can be E_TAG.
- * The next not void item must be END.
- * action:
- * The first not void action should be VF or PF.
- * The next not void action should be END.
- * pattern example:
- * ITEM		Spec			Mask
- * E_TAG	grp		0x1	0x3
-		e_cid_base	0x309	0xFFF
- * END
- * other members in mask and spec should set to 0x00.
- * item->last should be NULL.
- */
-static int
-cons_parse_l2_tn_filter(struct rte_eth_dev *dev,
-			const struct rte_flow_item pattern[],
-			const struct rte_flow_action *action,
-			struct ixgbe_l2_tunnel_conf *filter,
-			struct rte_flow_error *error)
-{
-	const struct rte_flow_item *item;
-	const struct rte_flow_item_e_tag *e_tag_spec;
-	const struct rte_flow_item_e_tag *e_tag_mask;
-	struct ixgbe_adapter *ad = IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-
-	/* The first not void item should be e-tag. */
-	item = next_no_void_pattern(pattern, NULL);
-	if (item->type != RTE_FLOW_ITEM_TYPE_E_TAG) {
-		memset(filter, 0, sizeof(struct ixgbe_l2_tunnel_conf));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Not supported by L2 tunnel filter");
-		return -rte_errno;
-	}
-
-	if (!item->spec || !item->mask) {
-		memset(filter, 0, sizeof(struct ixgbe_l2_tunnel_conf));
-		rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Not supported by L2 tunnel filter");
-		return -rte_errno;
-	}
-
-	/*Not supported last point for range*/
-	if (item->last) {
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
-			item, "Not supported last point for range");
-		return -rte_errno;
-	}
-
-	e_tag_spec = item->spec;
-	e_tag_mask = item->mask;
-
-	/* Only care about GRP and E cid base. */
-	if (e_tag_mask->epcp_edei_in_ecid_b ||
-	    e_tag_mask->in_ecid_e ||
-	    e_tag_mask->ecid_e ||
-	    e_tag_mask->rsvd_grp_ecid_b != rte_cpu_to_be_16(0x3FFF)) {
-		memset(filter, 0, sizeof(struct ixgbe_l2_tunnel_conf));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Not supported by L2 tunnel filter");
-		return -rte_errno;
-	}
-
-	filter->l2_tunnel_type = RTE_ETH_L2_TUNNEL_TYPE_E_TAG;
-	/**
-	 * grp and e_cid_base are bit fields and only use 14 bits.
-	 * e-tag id is taken as little endian by HW.
-	 */
-	filter->tunnel_id = rte_be_to_cpu_16(e_tag_spec->rsvd_grp_ecid_b);
-
-	/* check if the next not void item is END */
-	item = next_no_void_pattern(pattern, item);
-	if (item->type != RTE_FLOW_ITEM_TYPE_END) {
-		memset(filter, 0, sizeof(struct ixgbe_l2_tunnel_conf));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Not supported by L2 tunnel filter");
-		return -rte_errno;
-	}
-
-	if (action->type == RTE_FLOW_ACTION_TYPE_VF) {
-		const struct rte_flow_action_vf *act_vf = action->conf;
-		filter->pool = act_vf->id;
-	} else {
-		filter->pool = ad->max_vfs;
-	}
-
-	return 0;
-}
-
-static int
-ixgbe_parse_l2_tn_filter(struct rte_eth_dev *dev,
-			const struct rte_flow_attr *attr,
-			const struct rte_flow_item pattern[],
-			const struct rte_flow_action actions[],
-			struct ixgbe_l2_tunnel_conf *l2_tn_filter,
-			struct rte_flow_error *error)
-{
-	struct rte_eth_dev_data *dev_data = dev->data;
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev_data->dev_private);
-	struct ci_flow_actions parsed_actions;
-	struct ci_flow_actions_check_param ap_param = {
-		.allowed_types = (const enum rte_flow_action_type[]){
-			/* only vf/pf is allowed here */
-			RTE_FLOW_ACTION_TYPE_VF,
-			RTE_FLOW_ACTION_TYPE_PF,
-			RTE_FLOW_ACTION_TYPE_END
-		},
-		.driver_ctx = dev_data,
-		.check = ixgbe_flow_actions_check,
-		.max_actions = 1,
-	};
-	int ret = 0;
-	const struct rte_flow_action *action;
-
-	if (hw->mac.type != ixgbe_mac_X550 &&
-		hw->mac.type != ixgbe_mac_X550EM_x &&
-		hw->mac.type != ixgbe_mac_X550EM_a &&
-		hw->mac.type != ixgbe_mac_E610) {
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			NULL, "Not supported by L2 tunnel filter");
-		return -rte_errno;
-	}
-
-	/* validate attributes */
-	ret = ci_flow_check_attr(attr, NULL, error);
-	if (ret)
-		return ret;
-
-	/* parse requested actions */
-	ret = ci_flow_check_actions(actions, &ap_param, &parsed_actions, error);
-	if (ret)
-		return ret;
-
-	/* only one action is supported */
-	if (parsed_actions.count > 1) {
-		return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
-					  parsed_actions.actions[1],
-					  "Only one action can be specified at a time");
-	}
-	action = parsed_actions.actions[0];
-
-	ret = cons_parse_l2_tn_filter(dev, pattern, action, l2_tn_filter, error);
-
-	return ret;
-}
-
 /* search next no void pattern and skip fuzzy */
 static inline
 const struct rte_flow_item *next_no_fuzzy_pattern(
@@ -2450,13 +2291,11 @@ ixgbe_flow_create(struct rte_eth_dev *dev,
 		IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct rte_eth_ntuple_filter ntuple_filter;
 	struct ixgbe_fdir_rule fdir_rule;
-	struct ixgbe_l2_tunnel_conf l2_tn_filter;
 	struct ixgbe_hw_fdir_info *fdir_info =
 		IXGBE_DEV_PRIVATE_TO_FDIR_INFO(adapter);
 	struct ixgbe_rte_flow_rss_conf rss_conf;
 	struct rte_flow *flow = NULL;
 	struct ixgbe_ntuple_filter_ele *ntuple_filter_ptr;
-	struct ixgbe_eth_l2_tunnel_conf_ele *l2_tn_filter_ptr;
 	struct ixgbe_fdir_rule_ele *fdir_rule_ptr;
 	struct ixgbe_rss_conf_ele *rss_filter_ptr;
 	struct ixgbe_flow_mem *ixgbe_flow_mem_ptr;
@@ -2551,27 +2390,6 @@ ixgbe_flow_create(struct rte_eth_dev *dev,
 		return flow;
 	}
 
-	memset(&l2_tn_filter, 0, sizeof(struct ixgbe_l2_tunnel_conf));
-	ret = ixgbe_parse_l2_tn_filter(dev, attr, pattern,
-					actions, &l2_tn_filter, error);
-	if (!ret) {
-		ret = ixgbe_dev_l2_tunnel_filter_add(adapter, &l2_tn_filter, FALSE);
-		if (!ret) {
-			l2_tn_filter_ptr = rte_zmalloc("ixgbe_l2_tn_filter",
-				sizeof(struct ixgbe_eth_l2_tunnel_conf_ele), 0);
-			if (!l2_tn_filter_ptr) {
-				PMD_DRV_LOG(ERR, "failed to allocate memory");
-				goto out;
-			}
-			memcpy(&l2_tn_filter_ptr->filter_info,
-				&l2_tn_filter,
-				sizeof(struct ixgbe_l2_tunnel_conf));
-			flow->rule = l2_tn_filter_ptr;
-			flow->filter_type = RTE_ETH_FILTER_L2_TUNNEL;
-			return flow;
-		}
-	}
-
 	memset(&rss_conf, 0, sizeof(struct ixgbe_rte_flow_rss_conf));
 	ret = ixgbe_parse_rss_filter(dev, attr,
 					actions, &rss_conf, error);
@@ -2617,7 +2435,6 @@ ixgbe_flow_validate(struct rte_eth_dev *dev,
 {
 	struct ixgbe_adapter *ad = IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct rte_eth_ntuple_filter ntuple_filter;
-	struct ixgbe_l2_tunnel_conf l2_tn_filter;
 	struct ixgbe_fdir_rule fdir_rule;
 	struct ixgbe_rte_flow_rss_conf rss_conf;
 	int ret;
@@ -2648,12 +2465,6 @@ ixgbe_flow_validate(struct rte_eth_dev *dev,
 	if (!ret)
 		return 0;
 
-	memset(&l2_tn_filter, 0, sizeof(struct ixgbe_l2_tunnel_conf));
-	ret = ixgbe_parse_l2_tn_filter(dev, attr, pattern,
-				actions, &l2_tn_filter, error);
-	if (!ret)
-		return 0;
-
 	memset(&rss_conf, 0, sizeof(struct ixgbe_rte_flow_rss_conf));
 	ret = ixgbe_parse_rss_filter(dev, attr,
 					actions, &rss_conf, error);
@@ -2674,9 +2485,7 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev,
 	enum rte_filter_type filter_type = pmd_flow->filter_type;
 	struct rte_eth_ntuple_filter ntuple_filter;
 	struct ixgbe_fdir_rule fdir_rule;
-	struct ixgbe_l2_tunnel_conf l2_tn_filter;
 	struct ixgbe_ntuple_filter_ele *ntuple_filter_ptr;
-	struct ixgbe_eth_l2_tunnel_conf_ele *l2_tn_filter_ptr;
 	struct ixgbe_fdir_rule_ele *fdir_rule_ptr;
 	struct ixgbe_filter_ele_base *flow_mem_base;
 	struct ixgbe_hw_fdir_info *fdir_info =
@@ -2738,15 +2547,6 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev,
 			}
 		}
 		break;
-	case RTE_ETH_FILTER_L2_TUNNEL:
-		l2_tn_filter_ptr = (struct ixgbe_eth_l2_tunnel_conf_ele *)
-				pmd_flow->rule;
-		memcpy(&l2_tn_filter, &l2_tn_filter_ptr->filter_info,
-			sizeof(struct ixgbe_l2_tunnel_conf));
-		ret = ixgbe_dev_l2_tunnel_filter_del(adapter, &l2_tn_filter);
-		if (!ret)
-			rte_free(l2_tn_filter_ptr);
-		break;
 	case RTE_ETH_FILTER_HASH:
 		rss_filter_ptr = (struct ixgbe_rss_conf_ele *)
 				pmd_flow->rule;
drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index 453a23d3b6..ba0486b2c0 100644
@@ -17,5 +17,6 @@ extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 
 extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
 extern const struct ci_flow_engine ixgbe_syn_flow_engine;
+extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c new file +230 -0

230 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index bd9be0add3..0aaeb82a36 100644
@@ -13,6 +13,7 @@ sources += files(
         'ixgbe_flow.c',
         'ixgbe_flow_ethertype.c',
         'ixgbe_flow_syn.c',
+        'ixgbe_flow_l2tun.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#16 lore raw

PATCH 8/21 net/ixgbe: reimplement ntuple parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for ntuple.

---
 drivers/net/intel/ixgbe/ixgbe_flow.c        | 461 +-----------------
 drivers/net/intel/ixgbe/ixgbe_flow.h        |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c | 493 ++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build         |   1 +
 4 files changed, 496 insertions(+), 460 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c
drivers/net/intel/ixgbe/ixgbe_flow.c +1 -460
index ddcc7aa83c..a77c7694b3 100644
@@ -51,19 +51,12 @@
 #include "../common/flow_engine.h"
 #include "ixgbe_flow.h"
 
-#define IXGBE_MIN_N_TUPLE_PRIO 1
-#define IXGBE_MAX_N_TUPLE_PRIO 7
 #define IXGBE_MAX_FLX_SOURCE_OFF 62
 
 struct ixgbe_filter_ele_base {
 	TAILQ_ENTRY(ixgbe_filter_ele_base) entries;
 };
 
-/* ntuple filter list structure */
-struct ixgbe_ntuple_filter_ele {
-	struct ixgbe_filter_ele_base base;
-	struct rte_eth_ntuple_filter filter_info;
-};
 /* fdir filter list structure */
 struct ixgbe_fdir_rule_ele {
 	struct ixgbe_filter_ele_base base;
@@ -85,6 +78,7 @@ const struct ci_flow_engine_list ixgbe_flow_engine_list = {
 		&ixgbe_ethertype_flow_engine,
 		&ixgbe_syn_flow_engine,
 		&ixgbe_l2_tunnel_flow_engine,
+		&ixgbe_ntuple_flow_engine,
 	},
 };
 
@@ -162,355 +156,6 @@ ixgbe_flow_actions_check(const struct ci_flow_actions *actions,
  * normally the packets should use network order.
  */
 
-/**
- * Parse the rule to see if it is a n-tuple rule.
- * And get the n-tuple filter info BTW.
- * pattern:
- * The first not void item can be ETH or IPV4.
- * The second not void item must be IPV4 if the first one is ETH.
- * The third not void item must be UDP or TCP.
- * The next not void item must be END.
- * action:
- * The first not void action should be QUEUE.
- * The next not void action should be END.
- * pattern example:
- * ITEM		Spec			Mask
- * ETH		NULL			NULL
- * IPV4		src_addr 192.168.1.20	0xFFFFFFFF
- *		dst_addr 192.167.3.50	0xFFFFFFFF
- *		next_proto_id	17	0xFF
- * UDP/TCP/	src_port	80	0xFFFF
- * SCTP		dst_port	80	0xFFFF
- * END
- * other members in mask and spec should set to 0x00.
- * item->last should be NULL.
- *
- * Special case for flow action type RTE_FLOW_ACTION_TYPE_SECURITY.
- *
- */
-static int
-cons_parse_ntuple_filter(const struct rte_flow_attr *attr,
-			 const struct rte_flow_item pattern[],
-			 const struct rte_flow_action_queue *q_act,
-			 struct rte_eth_ntuple_filter *filter,
-			 struct rte_flow_error *error)
-{
-	const struct rte_flow_item *item;
-	const struct rte_flow_item_ipv4 *ipv4_spec;
-	const struct rte_flow_item_ipv4 *ipv4_mask;
-	const struct rte_flow_item_tcp *tcp_spec;
-	const struct rte_flow_item_tcp *tcp_mask;
-	const struct rte_flow_item_udp *udp_spec;
-	const struct rte_flow_item_udp *udp_mask;
-	const struct rte_flow_item_sctp *sctp_spec;
-	const struct rte_flow_item_sctp *sctp_mask;
-	const struct rte_flow_item_eth *eth_spec;
-	const struct rte_flow_item_eth *eth_mask;
-	const struct rte_flow_item_vlan *vlan_spec;
-	const struct rte_flow_item_vlan *vlan_mask;
-	struct rte_flow_item_eth eth_null;
-	struct rte_flow_item_vlan vlan_null;
-
-	/* Priority must be 16-bit */
-	if (attr->priority > UINT16_MAX) {
-		return rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, attr,
-				"Priority must be 16-bit");
-	}
-
-	memset(&eth_null, 0, sizeof(struct rte_flow_item_eth));
-	memset(&vlan_null, 0, sizeof(struct rte_flow_item_vlan));
-
-	/* the first not void item can be MAC or IPv4 */
-	item = next_no_void_pattern(pattern, NULL);
-
-	if (item->type != RTE_FLOW_ITEM_TYPE_ETH &&
-	    item->type != RTE_FLOW_ITEM_TYPE_IPV4) {
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Not supported by ntuple filter");
-		return -rte_errno;
-	}
-	/* Skip Ethernet */
-	if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
-		eth_spec = item->spec;
-		eth_mask = item->mask;
-		/*Not supported last point for range*/
-		if (item->last) {
-			rte_flow_error_set(error,
-			  EINVAL,
-			  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
-			  item, "Not supported last point for range");
-			return -rte_errno;
-
-		}
-		/* if the first item is MAC, the content should be NULL */
-		if ((item->spec != NULL && memcmp(eth_spec, &eth_null, sizeof(eth_null)) != 0) ||
-		    (item->mask != NULL && memcmp(eth_mask, &eth_null, sizeof(eth_null)) != 0)) {
-			rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, item,
-					"Not supported by ntuple filter");
-			return -rte_errno;
-		}
-		/* check if the next not void item is IPv4 or Vlan */
-		item = next_no_void_pattern(pattern, item);
-		if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
-			item->type != RTE_FLOW_ITEM_TYPE_VLAN) {
-			rte_flow_error_set(error,
-			  EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
-			  item, "Not supported by ntuple filter");
-			  return -rte_errno;
-		}
-	}
-
-	if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
-		vlan_spec = item->spec;
-		vlan_mask = item->mask;
-		/*Not supported last point for range*/
-		if (item->last) {
-			rte_flow_error_set(error,
-			  EINVAL,
-			  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
-			  item, "Not supported last point for range");
-			return -rte_errno;
-		}
-		/* the content should be NULL */
-		if ((item->spec != NULL && memcmp(vlan_spec, &vlan_null, sizeof(vlan_null)) != 0) ||
-		    (item->mask != NULL && memcmp(vlan_mask, &vlan_null, sizeof(vlan_null)) != 0)) {
-			rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, item,
-					"Not supported by ntuple filter");
-			return -rte_errno;
-		}
-		/* check if the next not void item is IPv4 */
-		item = next_no_void_pattern(pattern, item);
-		if (item->type != RTE_FLOW_ITEM_TYPE_IPV4) {
-			rte_flow_error_set(error,
-			  EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
-			  item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-	}
-
-	if (item->mask) {
-		/* get the IPv4 info */
-		if (!item->spec || !item->mask) {
-			rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Invalid ntuple mask");
-			return -rte_errno;
-		}
-		/*Not supported last point for range*/
-		if (item->last) {
-			rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
-				item, "Not supported last point for range");
-			return -rte_errno;
-		}
-
-		ipv4_mask = item->mask;
-		/**
-		 * Only support src & dst addresses, protocol,
-		 * others should be masked.
-		 */
-		if (ipv4_mask->hdr.version_ihl ||
-		    ipv4_mask->hdr.type_of_service ||
-		    ipv4_mask->hdr.total_length ||
-		    ipv4_mask->hdr.packet_id ||
-		    ipv4_mask->hdr.fragment_offset ||
-		    ipv4_mask->hdr.time_to_live ||
-		    ipv4_mask->hdr.hdr_checksum) {
-			rte_flow_error_set(error,
-				EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-		if ((ipv4_mask->hdr.src_addr != 0 &&
-			ipv4_mask->hdr.src_addr != UINT32_MAX) ||
-			(ipv4_mask->hdr.dst_addr != 0 &&
-			ipv4_mask->hdr.dst_addr != UINT32_MAX) ||
-			(ipv4_mask->hdr.next_proto_id != UINT8_MAX &&
-			ipv4_mask->hdr.next_proto_id != 0)) {
-			rte_flow_error_set(error,
-				EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-
-		filter->dst_ip_mask = ipv4_mask->hdr.dst_addr;
-		filter->src_ip_mask = ipv4_mask->hdr.src_addr;
-		filter->proto_mask  = ipv4_mask->hdr.next_proto_id;
-
-		ipv4_spec = item->spec;
-		filter->dst_ip = ipv4_spec->hdr.dst_addr;
-		filter->src_ip = ipv4_spec->hdr.src_addr;
-		filter->proto  = ipv4_spec->hdr.next_proto_id;
-	}
-
-	/* check if the next not void item is TCP or UDP */
-	item = next_no_void_pattern(pattern, item);
-	if (item->type != RTE_FLOW_ITEM_TYPE_TCP &&
-	    item->type != RTE_FLOW_ITEM_TYPE_UDP &&
-	    item->type != RTE_FLOW_ITEM_TYPE_SCTP &&
-	    item->type != RTE_FLOW_ITEM_TYPE_END) {
-		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Not supported by ntuple filter");
-		return -rte_errno;
-	}
-
-	if ((item->type != RTE_FLOW_ITEM_TYPE_END) &&
-		(!item->spec && !item->mask)) {
-		goto action;
-	}
-
-	/* get the TCP/UDP/SCTP info */
-	if (item->type != RTE_FLOW_ITEM_TYPE_END &&
-		(!item->spec || !item->mask)) {
-		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Invalid ntuple mask");
-		return -rte_errno;
-	}
-
-	/*Not supported last point for range*/
-	if (item->last) {
-		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
-			item, "Not supported last point for range");
-		return -rte_errno;
-
-	}
-
-	if (item->type == RTE_FLOW_ITEM_TYPE_TCP) {
-		tcp_mask = item->mask;
-
-		/**
-		 * Only support src & dst ports, tcp flags,
-		 * others should be masked.
-		 */
-		if (tcp_mask->hdr.sent_seq ||
-		    tcp_mask->hdr.recv_ack ||
-		    tcp_mask->hdr.data_off ||
-		    tcp_mask->hdr.rx_win ||
-		    tcp_mask->hdr.cksum ||
-		    tcp_mask->hdr.tcp_urp) {
-			memset(filter, 0,
-				sizeof(struct rte_eth_ntuple_filter));
-			rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-		if ((tcp_mask->hdr.src_port != 0 &&
-			tcp_mask->hdr.src_port != UINT16_MAX) ||
-			(tcp_mask->hdr.dst_port != 0 &&
-			tcp_mask->hdr.dst_port != UINT16_MAX)) {
-			rte_flow_error_set(error,
-				EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-
-		filter->dst_port_mask  = tcp_mask->hdr.dst_port;
-		filter->src_port_mask  = tcp_mask->hdr.src_port;
-		if (tcp_mask->hdr.tcp_flags == 0xFF) {
-			filter->flags |= RTE_NTUPLE_FLAGS_TCP_FLAG;
-		} else if (!tcp_mask->hdr.tcp_flags) {
-			filter->flags &= ~RTE_NTUPLE_FLAGS_TCP_FLAG;
-		} else {
-			memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-			rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-
-		tcp_spec = item->spec;
-		filter->dst_port  = tcp_spec->hdr.dst_port;
-		filter->src_port  = tcp_spec->hdr.src_port;
-		filter->tcp_flags = tcp_spec->hdr.tcp_flags;
-	} else if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
-		udp_mask = item->mask;
-
-		/**
-		 * Only support src & dst ports,
-		 * others should be masked.
-		 */
-		if (udp_mask->hdr.dgram_len ||
-		    udp_mask->hdr.dgram_cksum) {
-			memset(filter, 0,
-				sizeof(struct rte_eth_ntuple_filter));
-			rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-		if ((udp_mask->hdr.src_port != 0 &&
-			udp_mask->hdr.src_port != UINT16_MAX) ||
-			(udp_mask->hdr.dst_port != 0 &&
-			udp_mask->hdr.dst_port != UINT16_MAX)) {
-			rte_flow_error_set(error,
-				EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-
-		filter->dst_port_mask = udp_mask->hdr.dst_port;
-		filter->src_port_mask = udp_mask->hdr.src_port;
-
-		udp_spec = item->spec;
-		filter->dst_port = udp_spec->hdr.dst_port;
-		filter->src_port = udp_spec->hdr.src_port;
-	} else if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) {
-		sctp_mask = item->mask;
-
-		/**
-		 * Only support src & dst ports,
-		 * others should be masked.
-		 */
-		if (sctp_mask->hdr.tag ||
-		    sctp_mask->hdr.cksum) {
-			memset(filter, 0,
-				sizeof(struct rte_eth_ntuple_filter));
-			rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ITEM,
-				item, "Not supported by ntuple filter");
-			return -rte_errno;
-		}
-
-		filter->dst_port_mask = sctp_mask->hdr.dst_port;
-		filter->src_port_mask = sctp_mask->hdr.src_port;
-
-		sctp_spec = item->spec;
-		filter->dst_port = sctp_spec->hdr.dst_port;
-		filter->src_port = sctp_spec->hdr.src_port;
-	} else {
-		goto action;
-	}
-
-	/* check if the next not void item is END */
-	item = next_no_void_pattern(pattern, item);
-	if (item->type != RTE_FLOW_ITEM_TYPE_END) {
-		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ITEM,
-			item, "Not supported by ntuple filter");
-		return -rte_errno;
-	}
-
-action:
-
-	filter->queue = q_act->index;
-
-	filter->priority = (uint16_t)attr->priority;
-	if (attr->priority < IXGBE_MIN_N_TUPLE_PRIO || attr->priority > IXGBE_MAX_N_TUPLE_PRIO)
-		filter->priority = 1;
-
-	return 0;
-}
-
 static int
 ixgbe_parse_security_filter(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 		const struct rte_flow_item pattern[], const struct rte_flow_action actions[],
@@ -599,66 +244,6 @@ ixgbe_parse_security_filter(struct rte_eth_dev *dev, const struct rte_flow_attr
 	return 0;
 }
 
-/* a specific function for ixgbe because the flags is specific */
-static int
-ixgbe_parse_ntuple_filter(struct rte_eth_dev *dev,
-			  const struct rte_flow_attr *attr,
-			  const struct rte_flow_item pattern[],
-			  const struct rte_flow_action actions[],
-			  struct rte_eth_ntuple_filter *filter,
-			  struct rte_flow_error *error)
-{
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ci_flow_attr_check_param attr_param = {
-		.allow_priority = true,
-	};
-	struct ci_flow_actions parsed_actions;
-	struct ci_flow_actions_check_param ap_param = {
-		.allowed_types = (const enum rte_flow_action_type[]){
-			/* only queue is allowed here */
-			RTE_FLOW_ACTION_TYPE_QUEUE,
-			RTE_FLOW_ACTION_TYPE_END
-		},
-		.driver_ctx = dev->data,
-		.check = ixgbe_flow_actions_check,
-		.max_actions = 1,
-	};
-	const struct rte_flow_action *action;
-	int ret;
-
-	if (hw->mac.type != ixgbe_mac_82599EB &&
-			hw->mac.type != ixgbe_mac_X540)
-		return -ENOTSUP;
-
-	/* validate attributes */
-	ret = ci_flow_check_attr(attr, &attr_param, error);
-	if (ret)
-		return ret;
-
-	/* parse requested actions */
-	ret = ci_flow_check_actions(actions, &ap_param, &parsed_actions, error);
-	if (ret)
-		return ret;
-	action = parsed_actions.actions[0];
-
-	ret = cons_parse_ntuple_filter(attr, pattern, action->conf, filter, error);
-	if (ret)
-		return ret;
-
-	/* Ixgbe doesn't support tcp flags. */
-	if (filter->flags & RTE_NTUPLE_FLAGS_TCP_FLAG) {
-		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   NULL, "Not supported by ntuple filter");
-		return -rte_errno;
-	}
-
-	/* fixed value for ixgbe */
-	filter->flags = RTE_5TUPLE_FLAGS;
-	return 0;
-}
-
 /* search next no void pattern and skip fuzzy */
 static inline
 const struct rte_flow_item *next_no_fuzzy_pattern(
@@ -2289,13 +1874,11 @@ ixgbe_flow_create(struct rte_eth_dev *dev,
 	int ret;
 	struct ixgbe_adapter *adapter =
 		IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	struct rte_eth_ntuple_filter ntuple_filter;
 	struct ixgbe_fdir_rule fdir_rule;
 	struct ixgbe_hw_fdir_info *fdir_info =
 		IXGBE_DEV_PRIVATE_TO_FDIR_INFO(adapter);
 	struct ixgbe_rte_flow_rss_conf rss_conf;
 	struct rte_flow *flow = NULL;
-	struct ixgbe_ntuple_filter_ele *ntuple_filter_ptr;
 	struct ixgbe_fdir_rule_ele *fdir_rule_ptr;
 	struct ixgbe_rss_conf_ele *rss_filter_ptr;
 	struct ixgbe_flow_mem *ixgbe_flow_mem_ptr;
@@ -2332,29 +1915,6 @@ ixgbe_flow_create(struct rte_eth_dev *dev,
 		return flow;
 	}
 
-	memset(&ntuple_filter, 0, sizeof(struct rte_eth_ntuple_filter));
-	ret = ixgbe_parse_ntuple_filter(dev, attr, pattern,
-			actions, &ntuple_filter, error);
-
-	if (!ret) {
-		ret = ixgbe_add_del_ntuple_filter(adapter, &ntuple_filter, TRUE);
-		if (!ret) {
-			ntuple_filter_ptr = rte_zmalloc("ixgbe_ntuple_filter",
-				sizeof(struct ixgbe_ntuple_filter_ele), 0);
-			if (!ntuple_filter_ptr) {
-				PMD_DRV_LOG(ERR, "failed to allocate memory");
-				goto out;
-			}
-			memcpy(&ntuple_filter_ptr->filter_info,
-				&ntuple_filter,
-				sizeof(struct rte_eth_ntuple_filter));
-			flow->rule = ntuple_filter_ptr;
-			flow->filter_type = RTE_ETH_FILTER_NTUPLE;
-			return flow;
-		}
-		goto out;
-	}
-
 	memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule));
 	ret = ixgbe_parse_fdir_filter(dev, attr, pattern,
 				actions, &fdir_rule, error);
@@ -2434,7 +1994,6 @@ ixgbe_flow_validate(struct rte_eth_dev *dev,
 		struct rte_flow_error *error)
 {
 	struct ixgbe_adapter *ad = IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	struct rte_eth_ntuple_filter ntuple_filter;
 	struct ixgbe_fdir_rule fdir_rule;
 	struct ixgbe_rte_flow_rss_conf rss_conf;
 	int ret;
@@ -2453,12 +2012,6 @@ ixgbe_flow_validate(struct rte_eth_dev *dev,
 	if (!ret)
 		return 0;
 
-	memset(&ntuple_filter, 0, sizeof(struct rte_eth_ntuple_filter));
-	ret = ixgbe_parse_ntuple_filter(dev, attr, pattern,
-				actions, &ntuple_filter, error);
-	if (!ret)
-		return 0;
-
 	memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule));
 	ret = ixgbe_parse_fdir_filter(dev, attr, pattern,
 				actions, &fdir_rule, error);
@@ -2483,9 +2036,7 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev,
 		IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct rte_flow *pmd_flow = flow;
 	enum rte_filter_type filter_type = pmd_flow->filter_type;
-	struct rte_eth_ntuple_filter ntuple_filter;
 	struct ixgbe_fdir_rule fdir_rule;
-	struct ixgbe_ntuple_filter_ele *ntuple_filter_ptr;
 	struct ixgbe_fdir_rule_ele *fdir_rule_ptr;
 	struct ixgbe_filter_ele_base *flow_mem_base;
 	struct ixgbe_hw_fdir_info *fdir_info =
@@ -2521,16 +2072,6 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev,
 	}
 
 	switch (filter_type) {
-	case RTE_ETH_FILTER_NTUPLE:
-		ntuple_filter_ptr = (struct ixgbe_ntuple_filter_ele *)
-					pmd_flow->rule;
-		memcpy(&ntuple_filter,
-			&ntuple_filter_ptr->filter_info,
-			sizeof(struct rte_eth_ntuple_filter));
-		ret = ixgbe_add_del_ntuple_filter(adapter, &ntuple_filter, FALSE);
-		if (!ret)
-			rte_free(ntuple_filter_ptr);
-		break;
 	case RTE_ETH_FILTER_FDIR:
 		fdir_rule_ptr = (struct ixgbe_fdir_rule_ele *)pmd_flow->rule;
 		memcpy(&fdir_rule,
drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index ba0486b2c0..6f082e9402 100644
@@ -18,5 +18,6 @@ extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
 extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
+extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c new file +493 -0

493 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 0aaeb82a36..f3052daf4f 100644
@@ -14,6 +14,7 @@ sources += files(
         'ixgbe_flow_ethertype.c',
         'ixgbe_flow_syn.c',
         'ixgbe_flow_l2tun.c',
+        'ixgbe_flow_ntuple.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#17 lore raw

PATCH 9/21 net/ixgbe: reimplement security parser

In reply to Anatoly Burakov

Use the new flow graph API and common flow engine infrastructure to implement flow parser for security filter. As a result, flow item checks have become more stringent:

- Mask is now explicitly validated to not have unsupported items in it, when previously they were ignored
- Mask is also validated to mask src/dst addresses, as otherwise it is inconsistent with rte_flow API

Previously, security parser was a special case, now it is a first class citizen. A new callback for removing SA's through rte_flow API has been added to avoid leaking SA's when removing flows.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.h        |   2 -
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 120 +------
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_security.c | 320 ++++++++++++++++++
 drivers/net/intel/ixgbe/ixgbe_ipsec.c         |  54 ++-
 drivers/net/intel/ixgbe/ixgbe_ipsec.h         |   4 +-
 drivers/net/intel/ixgbe/meson.build           |   1 +
 7 files changed, 377 insertions(+), 125 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_security.c
drivers/net/intel/ixgbe/ixgbe_ethdev.h +0 -2
index cde0ee8fda..e08c1ebb52 100644
@@ -350,8 +350,6 @@ struct ixgbe_l2_tn_info {
 struct rte_flow {
 	struct ci_flow flow;
 	enum rte_filter_type filter_type;
-	/* security flows are not rte_filter_type */
-	bool is_security;
 	void *rule;
 };
 
drivers/net/intel/ixgbe/ixgbe_flow.c +2 -118

174 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index 6f082e9402..87cf028245 100644
@@ -19,5 +19,6 @@ extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
 extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
 extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
+extern const struct ci_flow_engine ixgbe_security_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_security.c new file +320 -0

320 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_ipsec.c +50 -4

95 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_ipsec.h +3 -1
index 1099b5f598..55ecd91c5d 100644
@@ -123,7 +123,9 @@ struct ip_spec {
 	} spec;
 };
 int ixgbe_crypto_add_ingress_sa_from_flow(struct rte_security_session *sess,
-		const struct ip_spec *ip_spec);
+		const struct ip_spec *ip_spec, uint32_t *sa_index);
+int ixgbe_crypto_remove_ingress_sa_from_flow(struct rte_security_session *sess,
+		uint32_t sa_index);
 
 
 
drivers/net/intel/ixgbe/meson.build +1 -0
index f3052daf4f..65ffe19939 100644
@@ -15,6 +15,7 @@ sources += files(
         'ixgbe_flow_syn.c',
         'ixgbe_flow_l2tun.c',
         'ixgbe_flow_ntuple.c',
+        'ixgbe_flow_security.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#18 lore raw

PATCH 10/21 net/ixgbe: reimplement FDIR parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for flow director.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.h    |    1 +
 drivers/net/intel/ixgbe/ixgbe_fdir.c      |    1 -
 drivers/net/intel/ixgbe/ixgbe_flow.c      | 1584 +--------------------
 drivers/net/intel/ixgbe/ixgbe_flow.h      |    2 +
 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c | 1553 ++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build       |    1 +
 6 files changed, 1560 insertions(+), 1582 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c
drivers/net/intel/ixgbe/ixgbe_ethdev.h +1 -0
index e08c1ebb52..b7c795611f 100644
@@ -50,6 +50,7 @@
 #define IXGBE_VMDQ_DCB_NB_QUEUES     IXGBE_MAX_RX_QUEUE_NUM
 #define IXGBE_DCB_NB_QUEUES          IXGBE_MAX_RX_QUEUE_NUM
 #define IXGBE_NONE_MODE_TX_NB_QUEUES 64
+#define IXGBE_MAX_FLX_SOURCE_OFF 62
 
 #ifndef NBBY
 #define NBBY	8	/* number of bits in a byte */
drivers/net/intel/ixgbe/ixgbe_fdir.c +0 -1
index b32dc54287..f3c0f334f5 100644
@@ -36,7 +36,6 @@
 #define SIG_BUCKET_256KB_HASH_MASK      0x7FFF  /* 15 bits */
 #define IXGBE_DEFAULT_FLEXBYTES_OFFSET  12 /* default flexbytes offset in bytes */
 #define IXGBE_FDIR_MAX_FLEX_LEN         2 /* len in bytes of flexbytes */
-#define IXGBE_MAX_FLX_SOURCE_OFF        62
 #define IXGBE_FDIRCTRL_FLEX_MASK        (0x1F << IXGBE_FDIRCTRL_FLEX_SHIFT)
 #define IXGBE_FDIRCMD_CMD_INTERVAL_US   10
 
drivers/net/intel/ixgbe/ixgbe_flow.c +3 -1581

1,647 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +2 -0
index 87cf028245..256e0478f5 100644
@@ -20,5 +20,7 @@ extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
 extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
 extern const struct ci_flow_engine ixgbe_security_flow_engine;
+extern const struct ci_flow_engine ixgbe_fdir_flow_engine;
+extern const struct ci_flow_engine ixgbe_fdir_tunnel_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_fdir.c new file +1553 -0

1,332 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 65ffe19939..770125350e 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.52.0

Part of the diff is not shown here. View the raw message.

#19 lore raw

PATCH 11/21 net/ixgbe: reimplement hash parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for RSS configuration.

RSS flow parser doesn't really parse any "flows", it was completely ignoring the flow patterns and was only looking at actions. It will therefore not specify a pattern graph and will thus match NULL pattern, empty patterns (START -> END), and ANY patterns (START -> ANY -> END).

RSS was the last engine using "filter list", so that is now removed.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c    |   6 -
 drivers/net/intel/ixgbe/ixgbe_ethdev.h    |   9 +-
 drivers/net/intel/ixgbe/ixgbe_flow.c      | 406 +---------------------
 drivers/net/intel/ixgbe/ixgbe_flow.h      |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_hash.c | 178 ++++++++++
 drivers/net/intel/ixgbe/meson.build       |   1 +
 6 files changed, 191 insertions(+), 410 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_hash.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +0 -6
index e8736be337..2116e7c164 100644
@@ -1335,9 +1335,6 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 	if (ret)
 		goto err_l2_tn_filter_init;
 
-	/* initialize flow filter lists */
-	ixgbe_filterlist_init(eth_dev);
-
 	/* initialize bandwidth configuration info */
 	memset(bw_conf, 0, sizeof(struct ixgbe_bw_conf));
 
@@ -3153,9 +3150,6 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	/* Remove all ntuple filters of the device */
 	ixgbe_ntuple_filter_uninit(dev);
 
-	/* clear all the filters list */
-	ixgbe_filterlist_flush(dev);
-
 	/* Remove all Traffic Manager configuration */
 	ixgbe_tm_conf_uninit(dev);
 
drivers/net/intel/ixgbe/ixgbe_ethdev.h +1 -8
index b7c795611f..1c18728f11 100644
@@ -348,10 +348,9 @@ struct ixgbe_l2_tn_info {
 	uint16_t e_tag_ether_type; /* ether type for e-tag */
 };
 
+/* no driver-specific data needed */
 struct rte_flow {
 	struct ci_flow flow;
-	enum rte_filter_type filter_type;
-	void *rule;
 };
 
 struct ixgbe_macsec_setting {
@@ -459,9 +458,6 @@ struct ixgbe_tm_conf {
 	bool committed;
 };
 
-struct ixgbe_filter_ele_base;
-TAILQ_HEAD(ixgbe_filter_ele_list, ixgbe_filter_ele_base);
-
 /*
  * Structure to store private data for each driver instance (for each port).
  */
@@ -483,7 +479,6 @@ struct ixgbe_adapter {
 	struct ixgbe_bypass_info    bps;
 #endif /* RTE_LIBRTE_IXGBE_BYPASS */
 	struct ixgbe_filter_info    filter;
-	struct ixgbe_filter_ele_list flow_list;
 	struct ixgbe_l2_tn_info     l2_tn;
 	struct ixgbe_bw_conf        bw_conf;
 	struct ixgbe_ipsec          ipsec;
@@ -704,8 +699,6 @@ ixgbe_dev_l2_tunnel_filter_add(struct ixgbe_adapter *adapter,
 int
 ixgbe_dev_l2_tunnel_filter_del(struct ixgbe_adapter *adapter,
 			       struct ixgbe_l2_tunnel_conf *l2_tunnel);
-void ixgbe_filterlist_init(struct rte_eth_dev *dev);
-void ixgbe_filterlist_flush(struct rte_eth_dev *dev);
 /*
  * Flow director function prototypes
  */
drivers/net/intel/ixgbe/ixgbe_flow.c +10 -396

479 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index 256e0478f5..959b2c9594 100644
@@ -22,5 +22,6 @@ extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
 extern const struct ci_flow_engine ixgbe_security_flow_engine;
 extern const struct ci_flow_engine ixgbe_fdir_flow_engine;
 extern const struct ci_flow_engine ixgbe_fdir_tunnel_flow_engine;
+extern const struct ci_flow_engine ixgbe_hash_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_hash.c new file +178 -0

178 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 770125350e..de35833e48 100644
@@ -17,6 +17,7 @@ sources += files(
         'ixgbe_flow_ntuple.c',
         'ixgbe_flow_security.c',
         'ixgbe_flow_fdir.c',
+        'ixgbe_flow_hash.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#20 lore raw

PATCH 12/21 net/i40e: add support for common flow parsing

In reply to Anatoly Burakov

Implement support for common flow parsing infrastructure in preparation for migration of flow engines.

---
 drivers/net/intel/i40e/i40e_ethdev.c | 12 +++++++
 drivers/net/intel/i40e/i40e_ethdev.h |  5 +++
 drivers/net/intel/i40e/i40e_flow.c   | 53 +++++++++++++++++++++++++++-
 drivers/net/intel/i40e/i40e_flow.h   | 12 +++++++
 4 files changed, 81 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow.h
drivers/net/intel/i40e/i40e_ethdev.c +12 -0
index b6b2d291ee..a9f6a5680b 100644
@@ -42,6 +42,9 @@
 #include "i40e_regs.h"
 #include "rte_pmd_i40e.h"
 #include "i40e_hash.h"
+#include "i40e_flow.h"
+
+#include "../common/flow_engine.h"
 
 #define ETH_I40E_FLOATING_VEB_ARG	"enable_floating_veb"
 #define ETH_I40E_FLOATING_VEB_LIST_ARG	"floating_veb_list"
@@ -1844,6 +1847,10 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	/* reset all stats of the device, including pf and main vsi */
 	i40e_dev_stats_reset(dev);
 
+	/* initialize flow engine configuration */
+	ci_flow_engine_conf_init(&pf->flow_engine_conf,
+			&i40e_flow_engine_list, dev->data);
+
 	return 0;
 
 err_init_fdir_filter_list:
@@ -2700,6 +2707,9 @@ i40e_dev_close(struct rte_eth_dev *dev)
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
 		return 0;
 
+	/* uninstall all flows while the HW is still alive */
+	ci_flow_flush(&pf->flow_engine_conf, NULL);
+
 	ret = rte_eth_switch_domain_free(pf->switch_domain_id);
 	if (ret)
 		PMD_INIT_LOG(WARNING, "failed to free switch domain: %d", ret);
@@ -2785,6 +2795,8 @@ i40e_dev_close(struct rte_eth_dev *dev)
 			rte_free(p_flow);
 	}
 
+	ci_flow_engine_conf_reset(&pf->flow_engine_conf);
+
 	/* release the fdir static allocated memory */
 	i40e_fdir_memory_cleanup(pf);
 
drivers/net/intel/i40e/i40e_ethdev.h +5 -0
index 1e64a2d280..16b67268f7 100644
@@ -21,6 +21,8 @@
 #include "base/i40e_type.h"
 #include "base/virtchnl.h"
 
+#include "../common/flow_engine.h"
+
 #define I40E_AQ_LEN               32
 #define I40E_AQ_BUF_SZ            4096
 /* Number of queues per TC should be one of 1, 2, 4, 8, 16, 32, 64 */
@@ -278,6 +280,7 @@ enum i40e_flxpld_layer_idx {
  * Struct to store flow created.
  */
 struct rte_flow {
+	struct ci_flow base;
 	TAILQ_ENTRY(rte_flow) node;
 	enum rte_filter_type filter_type;
 	void *rule;
@@ -1182,6 +1185,8 @@ struct i40e_pf {
 	/* The floating enable flag for the specific VF */
 	bool floating_veb_list[I40E_MAX_VF];
 	struct i40e_flow_list flow_list;
+	/* flow engine configuration */
+	struct ci_flow_engine_conf flow_engine_conf;
 	bool mpls_replace_flag;  /* 1 - MPLS filter replace is done */
 	bool gtp_replace_flag;   /* 1 - GTP-C/U filter replace is done */
 	bool qinq_replace_flag;  /* QINQ filter replace is done */
drivers/net/intel/i40e/i40e_flow.c +52 -1

107 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h new file +12 -0
new file mode 100644
index 0000000000..c958868661
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef _I40E_FLOW_H_
+#define _I40E_FLOW_H_
+
+#include "../common/flow_engine.h"
+
+extern const struct ci_flow_engine_list i40e_flow_engine_list;
+
+#endif /* _I40E_FLOW_H_ */
Signature · 1 line
2.52.0
#21 lore raw

PATCH 13/21 net/i40e: reimplement ethertype parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for Ethertype.

---
 drivers/net/intel/i40e/i40e_ethdev.h         |   1 -
 drivers/net/intel/i40e/i40e_flow.c           | 294 +------------------
 drivers/net/intel/i40e/i40e_flow.h           |   4 +
 drivers/net/intel/i40e/i40e_flow_ethertype.c | 250 ++++++++++++++++
 drivers/net/intel/i40e/meson.build           |   1 +
 5 files changed, 266 insertions(+), 284 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_ethertype.c
drivers/net/intel/i40e/i40e_ethdev.h +0 -1
index 16b67268f7..db04b7de22 100644
@@ -1325,7 +1325,6 @@ extern const struct rte_flow_ops i40e_flow_ops;
 
 struct i40e_filter_ctx {
 	union {
-		struct rte_eth_ethertype_filter ethertype_filter;
 		struct i40e_fdir_filter_conf fdir_filter;
 		struct i40e_tunnel_filter_conf consistent_tunnel_filter;
 		struct i40e_rte_flow_rss_conf rss_conf;
drivers/net/intel/i40e/i40e_flow.c +11 -283

382 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h +4 -0
index c958868661..11d13a76fe 100644
@@ -7,6 +7,10 @@
 
 #include "../common/flow_engine.h"
 
+int i40e_get_outer_vlan(struct i40e_pf *pf, uint16_t *tpid);
+
 extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
+extern const struct ci_flow_engine i40e_flow_engine_ethertype;
+
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_ethertype.c new file +250 -0

250 lines not on this page · raw message

drivers/net/intel/i40e/meson.build +1 -0
index bccae1ffc1..bff0518fc9 100644
@@ -25,6 +25,7 @@ sources += files(
         'i40e_pf.c',
         'i40e_fdir.c',
         'i40e_flow.c',
+        'i40e_flow_ethertype.c',
         'i40e_tm.c',
         'i40e_hash.c',
         'i40e_vf_representor.c',
Signature · 1 line
2.52.0
#22 lore raw

PATCH 14/21 net/i40e: reimplement FDIR parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for flow director.

As a result of transitioning to more formalized validation, some checks have become more stringent. In particular, some protocols (such as SCTP) were previously accepting non-zero or invalid masks and either ignoring them or misinterpreting them to mean "fully masked". This has now been corrected.

The FDIR engine in i40e has also relied on a custom memory allocation scheme for fdir flows - also migrated to the new infrastructure.

---
 drivers/net/intel/i40e/i40e_ethdev.c    |   48 -
 drivers/net/intel/i40e/i40e_ethdev.h    |   36 +-
 drivers/net/intel/i40e/i40e_fdir.c      |   78 +-
 drivers/net/intel/i40e/i40e_flow.c      | 1957 +----------------------
 drivers/net/intel/i40e/i40e_flow.h      |    5 +
 drivers/net/intel/i40e/i40e_flow_fdir.c | 1834 +++++++++++++++++++++
 drivers/net/intel/i40e/meson.build      |    1 +
 drivers/net/intel/i40e/rte_pmd_i40e.c   |    3 +-
 8 files changed, 1873 insertions(+), 2089 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_fdir.c
drivers/net/intel/i40e/i40e_ethdev.c +0 -48
index a9f6a5680b..476b828211 100644
@@ -1103,10 +1103,6 @@ i40e_init_fdir_filter_list(struct rte_eth_dev *dev)
 	uint32_t alloc = hw->func_caps.fd_filters_guaranteed;
 	uint32_t best = hw->func_caps.fd_filters_best_effort;
 	enum i40e_filter_pctype pctype;
-	struct rte_bitmap *bmp = NULL;
-	uint32_t bmp_size;
-	void *mem = NULL;
-	uint32_t i = 0;
 	int ret;
 
 	struct rte_hash_parameters fdir_hash_params = {
@@ -1163,50 +1159,8 @@ i40e_init_fdir_filter_list(struct rte_eth_dev *dev)
 
 	PMD_DRV_LOG(INFO, "FDIR guarantee space: %u, best_effort space %u.", alloc, best);
 
-	fdir_info->fdir_flow_pool.pool =
-			rte_zmalloc("i40e_fdir_entry",
-				sizeof(struct i40e_fdir_entry) *
-				fdir_info->fdir_space_size,
-				0);
-
-	if (!fdir_info->fdir_flow_pool.pool) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for bitmap flow!");
-		ret = -ENOMEM;
-		goto err_fdir_bitmap_flow_alloc;
-	}
-
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		fdir_info->fdir_flow_pool.pool[i].idx = i;
-
-	bmp_size =
-		rte_bitmap_get_memory_footprint(fdir_info->fdir_space_size);
-	mem = rte_zmalloc("fdir_bmap", bmp_size, RTE_CACHE_LINE_SIZE);
-	if (mem == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_mem_alloc;
-	}
-	bmp = rte_bitmap_init(fdir_info->fdir_space_size, mem, bmp_size);
-	if (bmp == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to initialization fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_bmp_alloc;
-	}
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		rte_bitmap_set(bmp, i);
-
-	fdir_info->fdir_flow_pool.bitmap = bmp;
-
 	return 0;
 
-err_fdir_bmp_alloc:
-	rte_free(mem);
-err_fdir_mem_alloc:
-	rte_free(fdir_info->fdir_flow_pool.pool);
-err_fdir_bitmap_flow_alloc:
 	rte_free(fdir_info->fdir_filter_array);
 err_fdir_filter_array_alloc:
 	rte_free(fdir_info->hash_map);
@@ -1940,8 +1894,6 @@ i40e_fdir_memory_cleanup(struct i40e_pf *pf)
 	/* flow director memory cleanup */
 	rte_free(fdir_info->hash_map);
 	rte_hash_free(fdir_info->hash_table);
-	rte_free(fdir_info->fdir_flow_pool.bitmap);
-	rte_free(fdir_info->fdir_flow_pool.pool);
 	rte_free(fdir_info->fdir_filter_array);
 }
 
drivers/net/intel/i40e/i40e_ethdev.h +8 -28
index db04b7de22..fb255b5c11 100644
@@ -522,6 +522,9 @@ struct i40e_vmdq_info {
 #define I40E_WORD(hi, lo) (uint16_t)((((hi) << 8) & 0xFF00) | ((lo) & 0xFF))
 #define I40E_FLEX_WORD_MASK(off) (0x80 >> (off))
 #define I40E_FDIR_IPv6_TC_OFFSET	20
+#define I40E_IPV6_TC_MASK	(0xFF << I40E_FDIR_IPv6_TC_OFFSET)
+#define I40E_IPV6_FRAG_HEADER	44
+#define I40E_VLAN_TCI_MASK	(RTE_VLAN_PRI_MASK | RTE_VLAN_DEI_MASK | RTE_VLAN_ID_MASK)
 
 /* A structure used to define the input for GTP flow */
 struct i40e_gtp_flow {
@@ -718,28 +721,12 @@ struct i40e_fdir_filter {
 	struct i40e_fdir_filter_conf fdir;
 };
 
-/* fdir memory pool entry */
-struct i40e_fdir_entry {
-	struct rte_flow flow;
-	uint32_t idx;
-};
-
-/* pre-allocated fdir memory pool */
-struct i40e_fdir_flow_pool {
-	/* a bitmap to manage the fdir pool */
-	struct rte_bitmap *bitmap;
-	/* the size the pool is pf->fdir->fdir_space_size */
-	struct i40e_fdir_entry *pool;
-};
-
-#define FLOW_TO_FLOW_BITMAP(f) \
-	container_of((f), struct i40e_fdir_entry, flow)
-
 TAILQ_HEAD(i40e_fdir_filter_list, i40e_fdir_filter);
 /*
  *  A structure used to define fields of a FDIR related info.
  */
 struct i40e_fdir_info {
+	uint64_t num_fdir_flows;
 	struct i40e_vsi *fdir_vsi;     /* pointer to fdir VSI structure */
 	uint16_t match_counter_index;  /* Statistic counter index used for fdir*/
 	struct ci_tx_queue *txq;
@@ -790,8 +777,6 @@ struct i40e_fdir_info {
 	uint32_t fdir_guarantee_free_space;
 	/* the fdir total guaranteed space */
 	uint32_t fdir_guarantee_total_space;
-	/* the pre-allocated pool of the rte_flow */
-	struct i40e_fdir_flow_pool fdir_flow_pool;
 
 	/* Mark if flex pit and mask is set */
 	bool flex_pit_flag[I40E_MAX_FLXPLD_LAYER];
@@ -1325,7 +1310,6 @@ extern const struct rte_flow_ops i40e_flow_ops;
 
 struct i40e_filter_ctx {
 	union {
-		struct i40e_fdir_filter_conf fdir_filter;
 		struct i40e_tunnel_filter_conf consistent_tunnel_filter;
 		struct i40e_rte_flow_rss_conf rss_conf;
 	};
@@ -1374,8 +1358,8 @@ void i40e_vsi_enable_queues_intr(struct i40e_vsi *vsi);
 const struct rte_memzone *i40e_memzone_reserve(const char *name,
 					uint32_t len,
 					int socket_id);
-int i40e_fdir_configure(struct rte_eth_dev *dev);
-void i40e_fdir_rx_proc_enable(struct rte_eth_dev *dev, bool on);
+int i40e_fdir_configure(struct i40e_pf *pf);
+void i40e_fdir_rx_proc_enable(struct rte_eth_dev_data *dev_data, bool on);
 void i40e_fdir_teardown(struct i40e_pf *pf);
 enum i40e_filter_pctype
 	i40e_flowtype_to_pctype(const struct i40e_adapter *adapter,
@@ -1422,11 +1406,7 @@ uint64_t i40e_get_default_input_set(uint16_t pctype);
 int i40e_ethertype_filter_set(struct i40e_pf *pf,
 			      struct rte_eth_ethertype_filter *filter,
 			      bool add);
-struct rte_flow *
-i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info);
-void i40e_fdir_entry_pool_put(struct i40e_fdir_info *fdir_info,
-		struct rte_flow *flow);
-int i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
+int i40e_flow_add_del_fdir_filter(struct i40e_pf *pf,
 			      const struct i40e_fdir_filter_conf *filter,
 			      bool add);
 int i40e_dev_tunnel_filter_set(struct i40e_pf *pf,
@@ -1435,7 +1415,7 @@ int i40e_dev_tunnel_filter_set(struct i40e_pf *pf,
 int i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
 				  struct i40e_tunnel_filter_conf *tunnel_filter,
 				  uint8_t add);
-int i40e_fdir_flush(struct rte_eth_dev *dev);
+int i40e_fdir_flush(struct i40e_pf *pf);
 int i40e_find_all_vlan_for_mac(struct i40e_vsi *vsi,
 			       struct i40e_macvlan_filter *mv_f,
 			       int num, struct rte_ether_addr *addr);
drivers/net/intel/i40e/i40e_fdir.c +13 -65

153 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.c +10 -1947

2,076 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h +5 -0
index 11d13a76fe..df5e133a5e 100644
@@ -8,9 +8,14 @@
 #include "../common/flow_engine.h"
 
 int i40e_get_outer_vlan(struct i40e_pf *pf, uint16_t *tpid);
+uint8_t
+i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
+		enum rte_flow_item_type item_type,
+		struct i40e_fdir_filter_conf *filter);
 
 extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
 extern const struct ci_flow_engine i40e_flow_engine_ethertype;
+extern const struct ci_flow_engine i40e_flow_engine_fdir;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_fdir.c new file +1834 -0

606 lines not on this page · raw message

drivers/net/intel/i40e/meson.build +1 -0
index bff0518fc9..0638f873dd 100644

Lines of this file are not shown. View the raw message.

drivers/net/intel/i40e/rte_pmd_i40e.c +2 -1
index 2e7943ef8b..6adf343bd2 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.52.0

Part of the diff is not shown here. View the raw message.

#23 lore raw

PATCH 15/21 net/i40e: reimplement tunnel QinQ parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for tunnel QinQ filters.

As a result of transitioning to more formalized VLAN validation, some checks have become more stringent:

- VLAN TCI mask is now required to be fully masked (all-ones); previously the mask was only checked for eth_proto and any non-zero vlan_tci mask value was silently accepted

---
 drivers/net/intel/i40e/i40e_flow.c        | 132 +--------
 drivers/net/intel/i40e/i40e_flow.h        |   1 +
 drivers/net/intel/i40e/i40e_flow_tunnel.c | 337 ++++++++++++++++++++++
 drivers/net/intel/i40e/meson.build        |   1 +
 4 files changed, 340 insertions(+), 131 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_tunnel.c
drivers/net/intel/i40e/i40e_flow.c +1 -131
index a3b82f9c62..f783f2ca01 100644
@@ -34,6 +34,7 @@ const struct ci_flow_engine_list i40e_flow_engine_list = {
 	{
 		&i40e_flow_engine_ethertype,
 		&i40e_flow_engine_fdir,
+		&i40e_flow_engine_tunnel_qinq,
 	}
 };
 
@@ -87,17 +88,6 @@ static int i40e_flow_parse_gtp_filter(struct rte_eth_dev *dev,
 static int i40e_flow_destroy_tunnel_filter(struct i40e_pf *pf,
 					   struct i40e_tunnel_filter *filter);
 static int i40e_flow_flush_tunnel_filter(struct i40e_pf *pf);
-static int
-i40e_flow_parse_qinq_filter(struct rte_eth_dev *dev,
-			      const struct rte_flow_item pattern[],
-			      const struct rte_flow_action actions[],
-			      struct rte_flow_error *error,
-			      struct i40e_filter_ctx *filter);
-static int
-i40e_flow_parse_qinq_pattern(struct rte_eth_dev *dev,
-			      const struct rte_flow_item *pattern,
-			      struct rte_flow_error *error,
-			      struct i40e_tunnel_filter_conf *filter);
 
 static int i40e_flow_parse_l4_cloud_filter(struct rte_eth_dev *dev,
 					   const struct rte_flow_item pattern[],
@@ -292,13 +282,6 @@ static enum rte_flow_item_type pattern_mpls_4[] = {
 	RTE_FLOW_ITEM_TYPE_END,
 };
 
-static enum rte_flow_item_type pattern_qinq_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static struct i40e_valid_pattern i40e_supported_patterns[] = {
 	/* VXLAN */
 	{ pattern_vxlan_1, i40e_flow_parse_vxlan_filter },
@@ -320,8 +303,6 @@ static struct i40e_valid_pattern i40e_supported_patterns[] = {
 	{ pattern_fdir_ipv4_gtpu, i40e_flow_parse_gtp_filter },
 	{ pattern_fdir_ipv6_gtpc, i40e_flow_parse_gtp_filter },
 	{ pattern_fdir_ipv6_gtpu, i40e_flow_parse_gtp_filter },
-	/* QINQ */
-	{ pattern_qinq_1, i40e_flow_parse_qinq_filter },
 	/* L4 over port */
 	{ pattern_fdir_ipv4_udp, i40e_flow_parse_l4_cloud_filter },
 	{ pattern_fdir_ipv4_tcp, i40e_flow_parse_l4_cloud_filter },
@@ -1711,117 +1692,6 @@ i40e_flow_parse_gtp_filter(struct rte_eth_dev *dev,
 	return ret;
 }
 
-/* 1. Last in item should be NULL as range is not supported.
- * 2. Supported filter types: QINQ.
- * 3. Mask of fields which need to be matched should be
- *    filled with 1.
- * 4. Mask of fields which needn't to be matched should be
- *    filled with 0.
- */
-static int
-i40e_flow_parse_qinq_pattern(__rte_unused struct rte_eth_dev *dev,
-			      const struct rte_flow_item *pattern,
-			      struct rte_flow_error *error,
-			      struct i40e_tunnel_filter_conf *filter)
-{
-	const struct rte_flow_item *item = pattern;
-	const struct rte_flow_item_vlan *vlan_spec = NULL;
-	const struct rte_flow_item_vlan *vlan_mask = NULL;
-	const struct rte_flow_item_vlan *i_vlan_spec = NULL;
-	const struct rte_flow_item_vlan *i_vlan_mask = NULL;
-	const struct rte_flow_item_vlan *o_vlan_spec = NULL;
-	const struct rte_flow_item_vlan *o_vlan_mask = NULL;
-
-	enum rte_flow_item_type item_type;
-	bool vlan_flag = 0;
-
-	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
-		if (item->last) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Not support range");
-			return -rte_errno;
-		}
-		item_type = item->type;
-		switch (item_type) {
-		case RTE_FLOW_ITEM_TYPE_ETH:
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ETH item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_VLAN:
-			vlan_spec = item->spec;
-			vlan_mask = item->mask;
-
-			if (!(vlan_spec && vlan_mask) ||
-			    vlan_mask->hdr.eth_proto) {
-				rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Invalid vlan item");
-				return -rte_errno;
-			}
-
-			if (!vlan_flag) {
-				o_vlan_spec = vlan_spec;
-				o_vlan_mask = vlan_mask;
-				vlan_flag = 1;
-			} else {
-				i_vlan_spec = vlan_spec;
-				i_vlan_mask = vlan_mask;
-				vlan_flag = 0;
-			}
-			break;
-
-		default:
-			break;
-		}
-	}
-
-	/* Get filter specification */
-	if (o_vlan_mask != NULL &&  i_vlan_mask != NULL) {
-		filter->outer_vlan = rte_be_to_cpu_16(o_vlan_spec->hdr.vlan_tci);
-		filter->inner_vlan = rte_be_to_cpu_16(i_vlan_spec->hdr.vlan_tci);
-	} else {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   NULL,
-					   "Invalid filter type");
-			return -rte_errno;
-	}
-
-	filter->tunnel_type = I40E_TUNNEL_TYPE_QINQ;
-	return 0;
-}
-
-static int
-i40e_flow_parse_qinq_filter(struct rte_eth_dev *dev,
-			      const struct rte_flow_item pattern[],
-			      const struct rte_flow_action actions[],
-			      struct rte_flow_error *error,
-			      struct i40e_filter_ctx *filter)
-{
-	struct i40e_tunnel_filter_conf *tunnel_filter = &filter->consistent_tunnel_filter;
-	int ret;
-
-	ret = i40e_flow_parse_qinq_pattern(dev, pattern,
-					     error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	ret = i40e_flow_parse_tunnel_action(dev, actions, error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	filter->type = RTE_ETH_FILTER_TUNNEL;
-
-	return ret;
-}
 
 static int
 i40e_flow_check(struct rte_eth_dev *dev,
drivers/net/intel/i40e/i40e_flow.h +1 -0
index df5e133a5e..9c2360c4ec 100644
@@ -17,5 +17,6 @@ extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
 extern const struct ci_flow_engine i40e_flow_engine_ethertype;
 extern const struct ci_flow_engine i40e_flow_engine_fdir;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_qinq;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_tunnel.c new file +337 -0

337 lines not on this page · raw message

drivers/net/intel/i40e/meson.build +1 -0
index 0638f873dd..9cff46b5e6 100644
@@ -27,6 +27,7 @@ sources += files(
         'i40e_flow.c',
         'i40e_flow_ethertype.c',
         'i40e_flow_fdir.c',
+        'i40e_flow_tunnel.c',
         'i40e_tm.c',
         'i40e_hash.c',
         'i40e_vf_representor.c',
Signature · 1 line
2.52.0
#24 lore raw

PATCH 16/21 net/i40e: reimplement VXLAN parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for VXLAN tunnels.

For VLAN nodes we will use existing VLAN node validation which is more stringent, so we no longer allow any non-zero vlan_tci mask, only full mask.

---
 drivers/net/intel/i40e/i40e_flow.c        | 295 +-------------------
 drivers/net/intel/i40e/i40e_flow.h        |   2 +
 drivers/net/intel/i40e/i40e_flow_tunnel.c | 310 ++++++++++++++++++++++
 3 files changed, 323 insertions(+), 284 deletions(-)
drivers/net/intel/i40e/i40e_flow.c +11 -284
index f783f2ca01..f73f4fd301 100644
@@ -35,6 +35,7 @@ const struct ci_flow_engine_list i40e_flow_engine_list = {
 		&i40e_flow_engine_ethertype,
 		&i40e_flow_engine_fdir,
 		&i40e_flow_engine_tunnel_qinq,
+		&i40e_flow_engine_tunnel_vxlan,
 	}
 };
 
@@ -65,11 +66,6 @@ static int i40e_flow_parse_tunnel_action(struct rte_eth_dev *dev,
 				 const struct rte_flow_action *actions,
 				 struct rte_flow_error *error,
 				 struct i40e_tunnel_filter_conf *filter);
-static int i40e_flow_parse_vxlan_filter(struct rte_eth_dev *dev,
-					const struct rte_flow_item pattern[],
-					const struct rte_flow_action actions[],
-					struct rte_flow_error *error,
-					struct i40e_filter_ctx *filter);
 static int i40e_flow_parse_nvgre_filter(struct rte_eth_dev *dev,
 					const struct rte_flow_item pattern[],
 					const struct rte_flow_action actions[],
@@ -178,44 +174,6 @@ static enum rte_flow_item_type pattern_fdir_ipv6_gtpu[] = {
 };
 
 /* Pattern matched tunnel filter */
-static enum rte_flow_item_type pattern_vxlan_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_VXLAN,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_vxlan_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_VXLAN,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_vxlan_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_VXLAN,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_vxlan_4[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_VXLAN,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static enum rte_flow_item_type pattern_nvgre_1[] = {
 	RTE_FLOW_ITEM_TYPE_ETH,
 	RTE_FLOW_ITEM_TYPE_IPV4,
@@ -283,11 +241,6 @@ static enum rte_flow_item_type pattern_mpls_4[] = {
 };
 
 static struct i40e_valid_pattern i40e_supported_patterns[] = {
-	/* VXLAN */
-	{ pattern_vxlan_1, i40e_flow_parse_vxlan_filter },
-	{ pattern_vxlan_2, i40e_flow_parse_vxlan_filter },
-	{ pattern_vxlan_3, i40e_flow_parse_vxlan_filter },
-	{ pattern_vxlan_4, i40e_flow_parse_vxlan_filter },
 	/* NVGRE */
 	{ pattern_nvgre_1, i40e_flow_parse_nvgre_filter },
 	{ pattern_nvgre_2, i40e_flow_parse_nvgre_filter },
@@ -901,253 +854,27 @@ i40e_flow_parse_l4_cloud_filter(struct rte_eth_dev *dev,
 	return ret;
 }
 
-static uint16_t i40e_supported_tunnel_filter_types[] = {
-	RTE_ETH_TUNNEL_FILTER_IMAC | RTE_ETH_TUNNEL_FILTER_TENID |
-	RTE_ETH_TUNNEL_FILTER_IVLAN,
-	RTE_ETH_TUNNEL_FILTER_IMAC | RTE_ETH_TUNNEL_FILTER_IVLAN,
-	RTE_ETH_TUNNEL_FILTER_IMAC | RTE_ETH_TUNNEL_FILTER_TENID,
-	RTE_ETH_TUNNEL_FILTER_OMAC | RTE_ETH_TUNNEL_FILTER_TENID |
-	RTE_ETH_TUNNEL_FILTER_IMAC,
-	RTE_ETH_TUNNEL_FILTER_IMAC,
-};
-
-static int
+int
 i40e_check_tunnel_filter_type(uint8_t filter_type)
 {
+	const uint16_t i40e_supported_tunnel_filter_types[] = {
+		RTE_ETH_TUNNEL_FILTER_IMAC | RTE_ETH_TUNNEL_FILTER_TENID |
+		RTE_ETH_TUNNEL_FILTER_IVLAN,
+		RTE_ETH_TUNNEL_FILTER_IMAC | RTE_ETH_TUNNEL_FILTER_IVLAN,
+		RTE_ETH_TUNNEL_FILTER_IMAC | RTE_ETH_TUNNEL_FILTER_TENID,
+		RTE_ETH_TUNNEL_FILTER_OMAC | RTE_ETH_TUNNEL_FILTER_TENID |
+		RTE_ETH_TUNNEL_FILTER_IMAC,
+		RTE_ETH_TUNNEL_FILTER_IMAC,
+	};
 	uint8_t i;
 
 	for (i = 0; i < RTE_DIM(i40e_supported_tunnel_filter_types); i++) {
 		if (filter_type == i40e_supported_tunnel_filter_types[i])
 			return 0;
 	}
-
 	return -1;
 }
 
-/* 1. Last in item should be NULL as range is not supported.
- * 2. Supported filter types: IMAC_IVLAN_TENID, IMAC_IVLAN,
- *    IMAC_TENID, OMAC_TENID_IMAC and IMAC.
- * 3. Mask of fields which need to be matched should be
- *    filled with 1.
- * 4. Mask of fields which needn't to be matched should be
- *    filled with 0.
- */
-static int
-i40e_flow_parse_vxlan_pattern(__rte_unused struct rte_eth_dev *dev,
-			      const struct rte_flow_item *pattern,
-			      struct rte_flow_error *error,
-			      struct i40e_tunnel_filter_conf *filter)
-{
-	const struct rte_flow_item *item = pattern;
-	const struct rte_flow_item_eth *eth_spec;
-	const struct rte_flow_item_eth *eth_mask;
-	const struct rte_flow_item_vxlan *vxlan_spec;
-	const struct rte_flow_item_vxlan *vxlan_mask;
-	const struct rte_flow_item_vlan *vlan_spec;
-	const struct rte_flow_item_vlan *vlan_mask;
-	uint8_t filter_type = 0;
-	bool is_vni_masked = 0;
-	uint8_t vni_mask[] = {0xFF, 0xFF, 0xFF};
-	enum rte_flow_item_type item_type;
-	bool vxlan_flag = 0;
-	uint32_t tenant_id_be = 0;
-	int ret;
-
-	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
-		if (item->last) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Not support range");
-			return -rte_errno;
-		}
-		item_type = item->type;
-		switch (item_type) {
-		case RTE_FLOW_ITEM_TYPE_ETH:
-			eth_spec = item->spec;
-			eth_mask = item->mask;
-
-			/* Check if ETH item is used for place holder.
-			 * If yes, both spec and mask should be NULL.
-			 * If no, both spec and mask shouldn't be NULL.
-			 */
-			if ((!eth_spec && eth_mask) ||
-			    (eth_spec && !eth_mask)) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ether spec/mask");
-				return -rte_errno;
-			}
-
-			if (eth_spec && eth_mask) {
-				/* DST address of inner MAC shouldn't be masked.
-				 * SRC address of Inner MAC should be masked.
-				 */
-				if (!rte_is_broadcast_ether_addr(&eth_mask->hdr.dst_addr) ||
-				    !rte_is_zero_ether_addr(&eth_mask->hdr.src_addr) ||
-				    eth_mask->hdr.ether_type) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ether spec/mask");
-					return -rte_errno;
-				}
-
-				if (!vxlan_flag) {
-					memcpy(&filter->outer_mac,
-						   &eth_spec->hdr.dst_addr,
-						   RTE_ETHER_ADDR_LEN);
-					filter_type |= RTE_ETH_TUNNEL_FILTER_OMAC;
-				} else {
-					memcpy(&filter->inner_mac,
-						   &eth_spec->hdr.dst_addr,
-						   RTE_ETHER_ADDR_LEN);
-					filter_type |= RTE_ETH_TUNNEL_FILTER_IMAC;
-				}
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_VLAN:
-			vlan_spec = item->spec;
-			vlan_mask = item->mask;
-			if (!(vlan_spec && vlan_mask) ||
-			    vlan_mask->hdr.eth_proto) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid vlan item");
-				return -rte_errno;
-			}
-
-			if (vlan_spec && vlan_mask) {
-				if (vlan_mask->hdr.vlan_tci ==
-				    rte_cpu_to_be_16(I40E_VLAN_TCI_MASK))
-					filter->inner_vlan =
-					      rte_be_to_cpu_16(vlan_spec->hdr.vlan_tci) &
-					      I40E_VLAN_TCI_MASK;
-				filter_type |= RTE_ETH_TUNNEL_FILTER_IVLAN;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV4:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV4;
-			/* IPv4 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv4 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV6:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV6;
-			/* IPv6 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv6 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_UDP:
-			/* UDP is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Invalid UDP item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_VXLAN:
-			vxlan_spec = item->spec;
-			vxlan_mask = item->mask;
-			/* Check if VXLAN item is used to describe protocol.
-			 * If yes, both spec and mask should be NULL.
-			 * If no, both spec and mask shouldn't be NULL.
-			 */
-			if ((!vxlan_spec && vxlan_mask) ||
-			    (vxlan_spec && !vxlan_mask)) {
-				rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Invalid VXLAN item");
-				return -rte_errno;
-			}
-
-			/* Check if VNI is masked. */
-			if (vxlan_spec && vxlan_mask) {
-				is_vni_masked =
-					!!memcmp(vxlan_mask->hdr.vni, vni_mask,
-						 RTE_DIM(vni_mask));
-				if (is_vni_masked) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid VNI mask");
-					return -rte_errno;
-				}
-
-				memcpy(((uint8_t *)&tenant_id_be + 1),
-					   vxlan_spec->hdr.vni, 3);
-				filter->tenant_id =
-					rte_be_to_cpu_32(tenant_id_be);
-				filter_type |= RTE_ETH_TUNNEL_FILTER_TENID;
-			}
-
-			vxlan_flag = 1;
-			break;
-		default:
-			break;
-		}
-	}
-
-	ret = i40e_check_tunnel_filter_type(filter_type);
-	if (ret < 0) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   NULL,
-				   "Invalid filter type");
-		return -rte_errno;
-	}
-	filter->filter_type = filter_type;
-
-	filter->tunnel_type = I40E_TUNNEL_TYPE_VXLAN;
-
-	return 0;
-}
-
-static int
-i40e_flow_parse_vxlan_filter(struct rte_eth_dev *dev,
-			     const struct rte_flow_item pattern[],
-			     const struct rte_flow_action actions[],
-			     struct rte_flow_error *error,
-			     struct i40e_filter_ctx *filter)
-{
-	struct i40e_tunnel_filter_conf *tunnel_filter = &filter->consistent_tunnel_filter;
-	int ret;
-
-	ret = i40e_flow_parse_vxlan_pattern(dev, pattern,
-					    error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	ret = i40e_flow_parse_tunnel_action(dev, actions, error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	filter->type = RTE_ETH_FILTER_TUNNEL;
-
-	return ret;
-}
-
 /* 1. Last in item should be NULL as range is not supported.
  * 2. Supported filter types: IMAC_IVLAN_TENID, IMAC_IVLAN,
  *    IMAC_TENID, OMAC_TENID_IMAC and IMAC.
drivers/net/intel/i40e/i40e_flow.h +2 -0
index 9c2360c4ec..f70fa860e7 100644
@@ -12,11 +12,13 @@ uint8_t
 i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
 		enum rte_flow_item_type item_type,
 		struct i40e_fdir_filter_conf *filter);
+int i40e_check_tunnel_filter_type(uint8_t filter_type);
 
 extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
 extern const struct ci_flow_engine i40e_flow_engine_ethertype;
 extern const struct ci_flow_engine i40e_flow_engine_fdir;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_qinq;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_vxlan;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_tunnel.c +310 -0

322 lines not on this page · raw message

Signature · 1 line
2.52.0
#25 lore raw

PATCH 17/21 net/i40e: reimplement NVGRE parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for NVGRE tunnels.

For VLAN nodes we will use existing VLAN node validation which is more stringent, so we no longer allow any non-zero vlan_tci mask, only full mask.

---
 drivers/net/intel/i40e/i40e_flow.c        | 290 +---------------------
 drivers/net/intel/i40e/i40e_flow.h        |   1 +
 drivers/net/intel/i40e/i40e_flow_tunnel.c | 206 +++++++++++++++
 3 files changed, 208 insertions(+), 289 deletions(-)
drivers/net/intel/i40e/i40e_flow.c +1 -289
index f73f4fd301..99d118e1be 100644
@@ -36,6 +36,7 @@ const struct ci_flow_engine_list i40e_flow_engine_list = {
 		&i40e_flow_engine_fdir,
 		&i40e_flow_engine_tunnel_qinq,
 		&i40e_flow_engine_tunnel_vxlan,
+		&i40e_flow_engine_tunnel_nvgre,
 	}
 };
 
@@ -66,11 +67,6 @@ static int i40e_flow_parse_tunnel_action(struct rte_eth_dev *dev,
 				 const struct rte_flow_action *actions,
 				 struct rte_flow_error *error,
 				 struct i40e_tunnel_filter_conf *filter);
-static int i40e_flow_parse_nvgre_filter(struct rte_eth_dev *dev,
-					const struct rte_flow_item pattern[],
-					const struct rte_flow_action actions[],
-					struct rte_flow_error *error,
-					struct i40e_filter_ctx *filter);
 static int i40e_flow_parse_mpls_filter(struct rte_eth_dev *dev,
 				       const struct rte_flow_item pattern[],
 				       const struct rte_flow_action actions[],
@@ -174,40 +170,6 @@ static enum rte_flow_item_type pattern_fdir_ipv6_gtpu[] = {
 };
 
 /* Pattern matched tunnel filter */
-static enum rte_flow_item_type pattern_nvgre_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_NVGRE,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_nvgre_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_NVGRE,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_nvgre_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_NVGRE,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_nvgre_4[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_NVGRE,
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static enum rte_flow_item_type pattern_mpls_1[] = {
 	RTE_FLOW_ITEM_TYPE_ETH,
 	RTE_FLOW_ITEM_TYPE_IPV4,
@@ -241,11 +203,6 @@ static enum rte_flow_item_type pattern_mpls_4[] = {
 };
 
 static struct i40e_valid_pattern i40e_supported_patterns[] = {
-	/* NVGRE */
-	{ pattern_nvgre_1, i40e_flow_parse_nvgre_filter },
-	{ pattern_nvgre_2, i40e_flow_parse_nvgre_filter },
-	{ pattern_nvgre_3, i40e_flow_parse_nvgre_filter },
-	{ pattern_nvgre_4, i40e_flow_parse_nvgre_filter },
 	/* MPLSoUDP & MPLSoGRE */
 	{ pattern_mpls_1, i40e_flow_parse_mpls_filter },
 	{ pattern_mpls_2, i40e_flow_parse_mpls_filter },
@@ -875,251 +832,6 @@ i40e_check_tunnel_filter_type(uint8_t filter_type)
 	return -1;
 }
 
-/* 1. Last in item should be NULL as range is not supported.
- * 2. Supported filter types: IMAC_IVLAN_TENID, IMAC_IVLAN,
- *    IMAC_TENID, OMAC_TENID_IMAC and IMAC.
- * 3. Mask of fields which need to be matched should be
- *    filled with 1.
- * 4. Mask of fields which needn't to be matched should be
- *    filled with 0.
- */
-static int
-i40e_flow_parse_nvgre_pattern(__rte_unused struct rte_eth_dev *dev,
-			      const struct rte_flow_item *pattern,
-			      struct rte_flow_error *error,
-			      struct i40e_tunnel_filter_conf *filter)
-{
-	const struct rte_flow_item *item = pattern;
-	const struct rte_flow_item_eth *eth_spec;
-	const struct rte_flow_item_eth *eth_mask;
-	const struct rte_flow_item_nvgre *nvgre_spec;
-	const struct rte_flow_item_nvgre *nvgre_mask;
-	const struct rte_flow_item_vlan *vlan_spec;
-	const struct rte_flow_item_vlan *vlan_mask;
-	enum rte_flow_item_type item_type;
-	uint8_t filter_type = 0;
-	bool is_tni_masked = 0;
-	uint8_t tni_mask[] = {0xFF, 0xFF, 0xFF};
-	bool nvgre_flag = 0;
-	uint32_t tenant_id_be = 0;
-	int ret;
-
-	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
-		if (item->last) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Not support range");
-			return -rte_errno;
-		}
-		item_type = item->type;
-		switch (item_type) {
-		case RTE_FLOW_ITEM_TYPE_ETH:
-			eth_spec = item->spec;
-			eth_mask = item->mask;
-
-			/* Check if ETH item is used for place holder.
-			 * If yes, both spec and mask should be NULL.
-			 * If no, both spec and mask shouldn't be NULL.
-			 */
-			if ((!eth_spec && eth_mask) ||
-			    (eth_spec && !eth_mask)) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ether spec/mask");
-				return -rte_errno;
-			}
-
-			if (eth_spec && eth_mask) {
-				/* DST address of inner MAC shouldn't be masked.
-				 * SRC address of Inner MAC should be masked.
-				 */
-				if (!rte_is_broadcast_ether_addr(&eth_mask->hdr.dst_addr) ||
-				    !rte_is_zero_ether_addr(&eth_mask->hdr.src_addr) ||
-				    eth_mask->hdr.ether_type) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ether spec/mask");
-					return -rte_errno;
-				}
-
-				if (!nvgre_flag) {
-					memcpy(&filter->outer_mac,
-						   &eth_spec->hdr.dst_addr,
-						   RTE_ETHER_ADDR_LEN);
-					filter_type |= RTE_ETH_TUNNEL_FILTER_OMAC;
-				} else {
-					memcpy(&filter->inner_mac,
-						   &eth_spec->hdr.dst_addr,
-						   RTE_ETHER_ADDR_LEN);
-					filter_type |= RTE_ETH_TUNNEL_FILTER_IMAC;
-				}
-			}
-
-			break;
-		case RTE_FLOW_ITEM_TYPE_VLAN:
-			vlan_spec = item->spec;
-			vlan_mask = item->mask;
-			if (!(vlan_spec && vlan_mask) ||
-			    vlan_mask->hdr.eth_proto) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid vlan item");
-				return -rte_errno;
-			}
-
-			if (vlan_spec && vlan_mask) {
-				if (vlan_mask->hdr.vlan_tci ==
-				    rte_cpu_to_be_16(I40E_VLAN_TCI_MASK))
-					filter->inner_vlan =
-					      rte_be_to_cpu_16(vlan_spec->hdr.vlan_tci) &
-					      I40E_VLAN_TCI_MASK;
-				filter_type |= RTE_ETH_TUNNEL_FILTER_IVLAN;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV4:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV4;
-			/* IPv4 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv4 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV6:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV6;
-			/* IPv6 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv6 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_NVGRE:
-			nvgre_spec = item->spec;
-			nvgre_mask = item->mask;
-			/* Check if NVGRE item is used to describe protocol.
-			 * If yes, both spec and mask should be NULL.
-			 * If no, both spec and mask shouldn't be NULL.
-			 */
-			if ((!nvgre_spec && nvgre_mask) ||
-			    (nvgre_spec && !nvgre_mask)) {
-				rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Invalid NVGRE item");
-				return -rte_errno;
-			}
-
-			if (nvgre_spec && nvgre_mask) {
-				is_tni_masked =
-					!!memcmp(nvgre_mask->tni, tni_mask,
-						 RTE_DIM(tni_mask));
-				if (is_tni_masked) {
-					rte_flow_error_set(error, EINVAL,
-						       RTE_FLOW_ERROR_TYPE_ITEM,
-						       item,
-						       "Invalid TNI mask");
-					return -rte_errno;
-				}
-				if (nvgre_mask->protocol &&
-					nvgre_mask->protocol != 0xFFFF) {
-					rte_flow_error_set(error, EINVAL,
-						RTE_FLOW_ERROR_TYPE_ITEM,
-						item,
-						"Invalid NVGRE item");
-					return -rte_errno;
-				}
-				if (nvgre_mask->c_k_s_rsvd0_ver &&
-					nvgre_mask->c_k_s_rsvd0_ver !=
-					rte_cpu_to_be_16(0xFFFF)) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid NVGRE item");
-					return -rte_errno;
-				}
-				if (nvgre_spec->c_k_s_rsvd0_ver !=
-					rte_cpu_to_be_16(0x2000) &&
-					nvgre_mask->c_k_s_rsvd0_ver) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid NVGRE item");
-					return -rte_errno;
-				}
-				if (nvgre_mask->protocol &&
-					nvgre_spec->protocol !=
-					rte_cpu_to_be_16(0x6558)) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid NVGRE item");
-					return -rte_errno;
-				}
-				memcpy(((uint8_t *)&tenant_id_be + 1),
-					   nvgre_spec->tni, 3);
-				filter->tenant_id =
-					rte_be_to_cpu_32(tenant_id_be);
-				filter_type |= RTE_ETH_TUNNEL_FILTER_TENID;
-			}
-
-			nvgre_flag = 1;
-			break;
-		default:
-			break;
-		}
-	}
-
-	ret = i40e_check_tunnel_filter_type(filter_type);
-	if (ret < 0) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   NULL,
-				   "Invalid filter type");
-		return -rte_errno;
-	}
-	filter->filter_type = filter_type;
-
-	filter->tunnel_type = I40E_TUNNEL_TYPE_NVGRE;
-
-	return 0;
-}
-
-static int
-i40e_flow_parse_nvgre_filter(struct rte_eth_dev *dev,
-			     const struct rte_flow_item pattern[],
-			     const struct rte_flow_action actions[],
-			     struct rte_flow_error *error,
-			     struct i40e_filter_ctx *filter)
-{
-	struct i40e_tunnel_filter_conf *tunnel_filter = &filter->consistent_tunnel_filter;
-	int ret;
-
-	ret = i40e_flow_parse_nvgre_pattern(dev, pattern,
-					    error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	ret = i40e_flow_parse_tunnel_action(dev, actions, error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	filter->type = RTE_ETH_FILTER_TUNNEL;
-
-	return ret;
-}
 
 /* 1. Last in item should be NULL as range is not supported.
  * 2. Supported filter types: MPLS label.
drivers/net/intel/i40e/i40e_flow.h +1 -0
index f70fa860e7..9a206983f1 100644
@@ -20,5 +20,6 @@ extern const struct ci_flow_engine i40e_flow_engine_ethertype;
 extern const struct ci_flow_engine i40e_flow_engine_fdir;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_qinq;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_vxlan;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_nvgre;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_tunnel.c +206 -0

218 lines not on this page · raw message

Signature · 1 line
2.52.0
#26 lore raw

PATCH 18/21 net/i40e: reimplement MPLS parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for MPLS tunnels.

---
 drivers/net/intel/i40e/i40e_flow.c        | 195 +---------------------
 drivers/net/intel/i40e/i40e_flow.h        |   1 +
 drivers/net/intel/i40e/i40e_flow_tunnel.c | 173 +++++++++++++++++++
 3 files changed, 175 insertions(+), 194 deletions(-)
drivers/net/intel/i40e/i40e_flow.c +1 -194
index 99d118e1be..3d94c04872 100644
@@ -37,6 +37,7 @@ const struct ci_flow_engine_list i40e_flow_engine_list = {
 		&i40e_flow_engine_tunnel_qinq,
 		&i40e_flow_engine_tunnel_vxlan,
 		&i40e_flow_engine_tunnel_nvgre,
+		&i40e_flow_engine_tunnel_mpls,
 	}
 };
 
@@ -67,11 +68,6 @@ static int i40e_flow_parse_tunnel_action(struct rte_eth_dev *dev,
 				 const struct rte_flow_action *actions,
 				 struct rte_flow_error *error,
 				 struct i40e_tunnel_filter_conf *filter);
-static int i40e_flow_parse_mpls_filter(struct rte_eth_dev *dev,
-				       const struct rte_flow_item pattern[],
-				       const struct rte_flow_action actions[],
-				       struct rte_flow_error *error,
-				       struct i40e_filter_ctx *filter);
 static int i40e_flow_parse_gtp_filter(struct rte_eth_dev *dev,
 				      const struct rte_flow_item pattern[],
 				      const struct rte_flow_action actions[],
@@ -169,45 +165,7 @@ static enum rte_flow_item_type pattern_fdir_ipv6_gtpu[] = {
 	RTE_FLOW_ITEM_TYPE_END,
 };
 
-/* Pattern matched tunnel filter */
-static enum rte_flow_item_type pattern_mpls_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_MPLS,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_mpls_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_MPLS,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_mpls_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_GRE,
-	RTE_FLOW_ITEM_TYPE_MPLS,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_mpls_4[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_GRE,
-	RTE_FLOW_ITEM_TYPE_MPLS,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static struct i40e_valid_pattern i40e_supported_patterns[] = {
-	/* MPLSoUDP & MPLSoGRE */
-	{ pattern_mpls_1, i40e_flow_parse_mpls_filter },
-	{ pattern_mpls_2, i40e_flow_parse_mpls_filter },
-	{ pattern_mpls_3, i40e_flow_parse_mpls_filter },
-	{ pattern_mpls_4, i40e_flow_parse_mpls_filter },
 	/* GTP-C & GTP-U */
 	{ pattern_fdir_ipv4_gtpc, i40e_flow_parse_gtp_filter },
 	{ pattern_fdir_ipv4_gtpu, i40e_flow_parse_gtp_filter },
@@ -833,157 +791,6 @@ i40e_check_tunnel_filter_type(uint8_t filter_type)
 }
 
 
-/* 1. Last in item should be NULL as range is not supported.
- * 2. Supported filter types: MPLS label.
- * 3. Mask of fields which need to be matched should be
- *    filled with 1.
- * 4. Mask of fields which needn't to be matched should be
- *    filled with 0.
- */
-static int
-i40e_flow_parse_mpls_pattern(__rte_unused struct rte_eth_dev *dev,
-			     const struct rte_flow_item *pattern,
-			     struct rte_flow_error *error,
-			     struct i40e_tunnel_filter_conf *filter)
-{
-	const struct rte_flow_item *item = pattern;
-	const struct rte_flow_item_mpls *mpls_spec;
-	const struct rte_flow_item_mpls *mpls_mask;
-	enum rte_flow_item_type item_type;
-	bool is_mplsoudp = 0; /* 1 - MPLSoUDP, 0 - MPLSoGRE */
-	const uint8_t label_mask[3] = {0xFF, 0xFF, 0xF0};
-	uint32_t label_be = 0;
-
-	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
-		if (item->last) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Not support range");
-			return -rte_errno;
-		}
-		item_type = item->type;
-		switch (item_type) {
-		case RTE_FLOW_ITEM_TYPE_ETH:
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ETH item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV4:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV4;
-			/* IPv4 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv4 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV6:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV6;
-			/* IPv6 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv6 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_UDP:
-			/* UDP is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid UDP item");
-				return -rte_errno;
-			}
-			is_mplsoudp = 1;
-			break;
-		case RTE_FLOW_ITEM_TYPE_GRE:
-			/* GRE is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid GRE item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_MPLS:
-			mpls_spec = item->spec;
-			mpls_mask = item->mask;
-
-			if (!mpls_spec || !mpls_mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid MPLS item");
-				return -rte_errno;
-			}
-
-			if (memcmp(mpls_mask->label_tc_s, label_mask, 3)) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid MPLS label mask");
-				return -rte_errno;
-			}
-			memcpy(((uint8_t *)&label_be + 1),
-				   mpls_spec->label_tc_s, 3);
-			filter->tenant_id = rte_be_to_cpu_32(label_be) >> 4;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (is_mplsoudp)
-		filter->tunnel_type = I40E_TUNNEL_TYPE_MPLSoUDP;
-	else
-		filter->tunnel_type = I40E_TUNNEL_TYPE_MPLSoGRE;
-
-	return 0;
-}
-
-static int
-i40e_flow_parse_mpls_filter(struct rte_eth_dev *dev,
-			    const struct rte_flow_item pattern[],
-			    const struct rte_flow_action actions[],
-			    struct rte_flow_error *error,
-			    struct i40e_filter_ctx *filter)
-{
-	struct i40e_tunnel_filter_conf *tunnel_filter = &filter->consistent_tunnel_filter;
-	int ret;
-
-	ret = i40e_flow_parse_mpls_pattern(dev, pattern,
-					   error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	ret = i40e_flow_parse_tunnel_action(dev, actions, error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	filter->type = RTE_ETH_FILTER_TUNNEL;
-
-	return ret;
-}
-
 /* 1. Last in item should be NULL as range is not supported.
  * 2. Supported filter types: GTP TEID.
  * 3. Mask of fields which need to be matched should be
drivers/net/intel/i40e/i40e_flow.h +1 -0
index 9a206983f1..a32e61ddd6 100644
@@ -21,5 +21,6 @@ extern const struct ci_flow_engine i40e_flow_engine_fdir;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_qinq;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_vxlan;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_nvgre;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_mpls;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_tunnel.c +173 -0

185 lines not on this page · raw message

Signature · 1 line
2.52.0
#27 lore raw

PATCH 19/21 net/i40e: reimplement gtp parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for GTP tunnels.

---
 drivers/net/intel/i40e/i40e_flow.c        | 191 +---------------------
 drivers/net/intel/i40e/i40e_flow.h        |   1 +
 drivers/net/intel/i40e/i40e_flow_tunnel.c | 174 ++++++++++++++++++++
 3 files changed, 176 insertions(+), 190 deletions(-)
drivers/net/intel/i40e/i40e_flow.c +1 -190
index 3d94c04872..358d87a3ea 100644
@@ -38,6 +38,7 @@ const struct ci_flow_engine_list i40e_flow_engine_list = {
 		&i40e_flow_engine_tunnel_vxlan,
 		&i40e_flow_engine_tunnel_nvgre,
 		&i40e_flow_engine_tunnel_mpls,
+		&i40e_flow_engine_tunnel_gtp,
 	}
 };
 
@@ -68,11 +69,6 @@ static int i40e_flow_parse_tunnel_action(struct rte_eth_dev *dev,
 				 const struct rte_flow_action *actions,
 				 struct rte_flow_error *error,
 				 struct i40e_tunnel_filter_conf *filter);
-static int i40e_flow_parse_gtp_filter(struct rte_eth_dev *dev,
-				      const struct rte_flow_item pattern[],
-				      const struct rte_flow_action actions[],
-				      struct rte_flow_error *error,
-				      struct i40e_filter_ctx *filter);
 static int i40e_flow_destroy_tunnel_filter(struct i40e_pf *pf,
 					   struct i40e_tunnel_filter *filter);
 static int i40e_flow_flush_tunnel_filter(struct i40e_pf *pf);
@@ -112,22 +108,6 @@ static enum rte_flow_item_type pattern_fdir_ipv4_sctp[] = {
 	RTE_FLOW_ITEM_TYPE_END,
 };
 
-static enum rte_flow_item_type pattern_fdir_ipv4_gtpc[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPC,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_gtpu[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPU,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static enum rte_flow_item_type pattern_fdir_ipv6_udp[] = {
 	RTE_FLOW_ITEM_TYPE_ETH,
 	RTE_FLOW_ITEM_TYPE_IPV6,
@@ -149,28 +129,7 @@ static enum rte_flow_item_type pattern_fdir_ipv6_sctp[] = {
 	RTE_FLOW_ITEM_TYPE_END,
 };
 
-static enum rte_flow_item_type pattern_fdir_ipv6_gtpc[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPC,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_gtpu[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPU,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static struct i40e_valid_pattern i40e_supported_patterns[] = {
-	/* GTP-C & GTP-U */
-	{ pattern_fdir_ipv4_gtpc, i40e_flow_parse_gtp_filter },
-	{ pattern_fdir_ipv4_gtpu, i40e_flow_parse_gtp_filter },
-	{ pattern_fdir_ipv6_gtpc, i40e_flow_parse_gtp_filter },
-	{ pattern_fdir_ipv6_gtpu, i40e_flow_parse_gtp_filter },
 	/* L4 over port */
 	{ pattern_fdir_ipv4_udp, i40e_flow_parse_l4_cloud_filter },
 	{ pattern_fdir_ipv4_tcp, i40e_flow_parse_l4_cloud_filter },
@@ -791,154 +750,6 @@ i40e_check_tunnel_filter_type(uint8_t filter_type)
 }
 
 
-/* 1. Last in item should be NULL as range is not supported.
- * 2. Supported filter types: GTP TEID.
- * 3. Mask of fields which need to be matched should be
- *    filled with 1.
- * 4. Mask of fields which needn't to be matched should be
- *    filled with 0.
- * 5. GTP profile supports GTPv1 only.
- * 6. GTP-C response message ('source_port' = 2123) is not supported.
- */
-static int
-i40e_flow_parse_gtp_pattern(struct rte_eth_dev *dev,
-			    const struct rte_flow_item *pattern,
-			    struct rte_flow_error *error,
-			    struct i40e_tunnel_filter_conf *filter)
-{
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	const struct rte_flow_item *item = pattern;
-	const struct rte_flow_item_gtp *gtp_spec;
-	const struct rte_flow_item_gtp *gtp_mask;
-	enum rte_flow_item_type item_type;
-
-	if (!pf->gtp_support) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   item,
-				   "GTP is not supported by default.");
-		return -rte_errno;
-	}
-
-	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
-		if (item->last) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Not support range");
-			return -rte_errno;
-		}
-		item_type = item->type;
-		switch (item_type) {
-		case RTE_FLOW_ITEM_TYPE_ETH:
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ETH item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV4:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV4;
-			/* IPv4 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv4 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV6:
-			filter->ip_type = I40E_TUNNEL_IPTYPE_IPV6;
-			/* IPv6 is used to describe protocol,
-			 * spec and mask should be NULL.
-			 */
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv6 item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_UDP:
-			if (item->spec || item->mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid UDP item");
-				return -rte_errno;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_GTPC:
-		case RTE_FLOW_ITEM_TYPE_GTPU:
-			gtp_spec = item->spec;
-			gtp_mask = item->mask;
-
-			if (!gtp_spec || !gtp_mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid GTP item");
-				return -rte_errno;
-			}
-
-			if (gtp_mask->hdr.gtp_hdr_info ||
-			    gtp_mask->hdr.msg_type ||
-			    gtp_mask->hdr.plen ||
-			    gtp_mask->hdr.teid != UINT32_MAX) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid GTP mask");
-				return -rte_errno;
-			}
-
-			if (item_type == RTE_FLOW_ITEM_TYPE_GTPC)
-				filter->tunnel_type = I40E_TUNNEL_TYPE_GTPC;
-			else if (item_type == RTE_FLOW_ITEM_TYPE_GTPU)
-				filter->tunnel_type = I40E_TUNNEL_TYPE_GTPU;
-
-			filter->tenant_id = rte_be_to_cpu_32(gtp_spec->hdr.teid);
-
-			break;
-		default:
-			break;
-		}
-	}
-
-	return 0;
-}
-
-static int
-i40e_flow_parse_gtp_filter(struct rte_eth_dev *dev,
-			   const struct rte_flow_item pattern[],
-			   const struct rte_flow_action actions[],
-			   struct rte_flow_error *error,
-			   struct i40e_filter_ctx *filter)
-{
-	struct i40e_tunnel_filter_conf *tunnel_filter = &filter->consistent_tunnel_filter;
-	int ret;
-
-	ret = i40e_flow_parse_gtp_pattern(dev, pattern,
-					  error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	ret = i40e_flow_parse_tunnel_action(dev, actions, error, tunnel_filter);
-	if (ret)
-		return ret;
-
-	filter->type = RTE_ETH_FILTER_TUNNEL;
-
-	return ret;
-}
-
-
 static int
 i40e_flow_check(struct rte_eth_dev *dev,
 		   const struct rte_flow_attr *attr,
drivers/net/intel/i40e/i40e_flow.h +1 -0
index a32e61ddd6..2d2a98333e 100644
@@ -22,5 +22,6 @@ extern const struct ci_flow_engine i40e_flow_engine_tunnel_qinq;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_vxlan;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_nvgre;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_mpls;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_gtp;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_tunnel.c +174 -0

186 lines not on this page · raw message

Signature · 1 line
2.52.0
#28 lore raw

PATCH 20/21 net/i40e: reimplement L4 cloud parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for L4 tunnels.

In addition to using the new graph infrastructure, some of the checks were made more stringent and/or more correct. In particular:

- old code did not check for whether fields other than ports are masked (they are now rejected)
- old code did not check for whether src/ports are fully masked (masks other than full are now rejected)
- old code used spec to decide which port to copy (as a result, it was not possible to match port 0 - this is now allowed)

Because the old parsing infrastructure is no longer needed (hash parser has always worked outside of it), it has been removed.

---
 drivers/net/intel/i40e/i40e_ethdev.h      |  15 +-
 drivers/net/intel/i40e/i40e_flow.c        | 615 +---------------------
 drivers/net/intel/i40e/i40e_flow.h        |   2 +-
 drivers/net/intel/i40e/i40e_flow_tunnel.c | 304 +++++++++++
 4 files changed, 308 insertions(+), 628 deletions(-)
drivers/net/intel/i40e/i40e_ethdev.h +1 -14
index fb255b5c11..29c6fcc5a7 100644
@@ -1309,23 +1309,10 @@ struct i40e_vf_representor {
 extern const struct rte_flow_ops i40e_flow_ops;
 
 struct i40e_filter_ctx {
-	union {
-		struct i40e_tunnel_filter_conf consistent_tunnel_filter;
-		struct i40e_rte_flow_rss_conf rss_conf;
-	};
+	struct i40e_rte_flow_rss_conf rss_conf;
 	enum rte_filter_type type;
 };
 
-typedef int (*parse_filter_t)(struct rte_eth_dev *dev,
-			      const struct rte_flow_item pattern[],
-			      const struct rte_flow_action actions[],
-			      struct rte_flow_error *error,
-			      struct i40e_filter_ctx *filter);
-struct i40e_valid_pattern {
-	enum rte_flow_item_type *items;
-	parse_filter_t parse_filter;
-};
-
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
 int i40e_vsi_release(struct i40e_vsi *vsi);
 struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf,
drivers/net/intel/i40e/i40e_flow.c +2 -613

683 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h +1 -1
index 2d2a98333e..de9f75bcdd 100644
@@ -12,7 +12,6 @@ uint8_t
 i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
 		enum rte_flow_item_type item_type,
 		struct i40e_fdir_filter_conf *filter);
-int i40e_check_tunnel_filter_type(uint8_t filter_type);
 
 extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
@@ -23,5 +22,6 @@ extern const struct ci_flow_engine i40e_flow_engine_tunnel_vxlan;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_nvgre;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_mpls;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_gtp;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_l4;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_tunnel.c +304 -0

322 lines not on this page · raw message

Signature · 1 line
2.52.0
#29 lore raw

PATCH 21/21 net/i40e: reimplement hash parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for RSS.

The RSS parser was bypassing the other generic infrastructure, probably because it was very convoluted and did not map onto that model very well. It has now been made a first-class citizen.

Additionally, this is the final engine in i40e, so remaining legacy flow infrastructure has been removed.

---
 drivers/net/intel/i40e/i40e_ethdev.c    |   11 -
 drivers/net/intel/i40e/i40e_ethdev.h    |   11 -
 drivers/net/intel/i40e/i40e_flow.c      |  299 +-----
 drivers/net/intel/i40e/i40e_flow.h      |    3 +
 drivers/net/intel/i40e/i40e_flow_hash.c | 1298 +++++++++++++++++++++++
 drivers/net/intel/i40e/i40e_hash.c      | 1037 +-----------------
 drivers/net/intel/i40e/i40e_hash.h      |    9 +-
 drivers/net/intel/i40e/meson.build      |    1 +
 8 files changed, 1325 insertions(+), 1344 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_hash.c
drivers/net/intel/i40e/i40e_ethdev.c +0 -11
index 476b828211..501a0d7409 100644
@@ -1979,8 +1979,6 @@ i40e_dev_configure(struct rte_eth_dev *dev)
 		}
 	}
 
-	TAILQ_INIT(&pf->flow_list);
-
 	return 0;
 
 err_dcb:
@@ -2648,7 +2646,6 @@ i40e_dev_close(struct rte_eth_dev *dev)
 	struct rte_pci_device *pci_dev = RTE_CLASS_TO_BUS_DEVICE(dev, *pci_dev);
 	struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
 	struct i40e_filter_control_settings settings;
-	struct rte_flow *p_flow;
 	uint32_t reg;
 	int i;
 	int ret;
@@ -2739,14 +2736,6 @@ i40e_dev_close(struct rte_eth_dev *dev)
 	i40e_rm_tunnel_filter_list(pf);
 	i40e_rm_fdir_filter_list(pf);
 
-	/* Remove all flows */
-	while ((p_flow = TAILQ_FIRST(&pf->flow_list))) {
-		TAILQ_REMOVE(&pf->flow_list, p_flow, node);
-		/* Do not free FDIR flows since they are static allocated */
-		if (p_flow->filter_type != RTE_ETH_FILTER_FDIR)
-			rte_free(p_flow);
-	}
-
 	ci_flow_engine_conf_reset(&pf->flow_engine_conf);
 
 	/* release the fdir static allocated memory */
drivers/net/intel/i40e/i40e_ethdev.h +0 -11
index 29c6fcc5a7..a114f4f7c3 100644
@@ -281,9 +281,6 @@ enum i40e_flxpld_layer_idx {
  */
 struct rte_flow {
 	struct ci_flow base;
-	TAILQ_ENTRY(rte_flow) node;
-	enum rte_filter_type filter_type;
-	void *rule;
 };
 
 /**
@@ -955,8 +952,6 @@ struct i40e_tunnel_filter_conf {
 	uint16_t vf_id;         /**< VF id, available when is_to_vf is 1. */
 };
 
-TAILQ_HEAD(i40e_flow_list, rte_flow);
-
 /* Struct to store Traffic Manager shaper profile. */
 struct i40e_tm_shaper_profile {
 	TAILQ_ENTRY(i40e_tm_shaper_profile) node;
@@ -1169,7 +1164,6 @@ struct i40e_pf {
 	bool floating_veb; /* The flag to use the floating VEB */
 	/* The floating enable flag for the specific VF */
 	bool floating_veb_list[I40E_MAX_VF];
-	struct i40e_flow_list flow_list;
 	/* flow engine configuration */
 	struct ci_flow_engine_conf flow_engine_conf;
 	bool mpls_replace_flag;  /* 1 - MPLS filter replace is done */
@@ -1308,11 +1302,6 @@ struct i40e_vf_representor {
 
 extern const struct rte_flow_ops i40e_flow_ops;
 
-struct i40e_filter_ctx {
-	struct i40e_rte_flow_rss_conf rss_conf;
-	enum rte_filter_type type;
-};
-
 int i40e_dev_switch_queues(struct i40e_pf *pf, bool on);
 int i40e_vsi_release(struct i40e_vsi *vsi);
 struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf,
drivers/net/intel/i40e/i40e_flow.c +10 -289

357 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h +3 -0
index de9f75bcdd..5178f080cd 100644
@@ -23,5 +23,8 @@ extern const struct ci_flow_engine i40e_flow_engine_tunnel_nvgre;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_mpls;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_gtp;
 extern const struct ci_flow_engine i40e_flow_engine_tunnel_l4;
+extern const struct ci_flow_engine i40e_flow_engine_hash_pattern;
+extern const struct ci_flow_engine i40e_flow_engine_hash_vlan;
+extern const struct ci_flow_engine i40e_flow_engine_hash_empty;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_hash.c new file +1298 -0

1,298 lines not on this page · raw message

drivers/net/intel/i40e/i40e_hash.c +12 -1025

1,081 lines not on this page · raw message

drivers/net/intel/i40e/i40e_hash.h +1 -8
index 3bf30cdee5..172ed130ec 100644
@@ -13,19 +13,12 @@
 extern "C" {
 #endif
 
-int i40e_hash_parse(struct rte_eth_dev *dev,
-		    const struct rte_flow_item pattern[],
-		    const struct rte_flow_action actions[],
-		    struct i40e_rte_flow_rss_conf *rss_conf,
-		    struct rte_flow_error *error);
-
 int i40e_hash_filter_create(struct i40e_pf *pf,
 			    struct i40e_rte_flow_rss_conf *rss_conf);
 
 int i40e_hash_filter_restore(struct i40e_pf *pf);
 int i40e_hash_filter_destroy(struct i40e_pf *pf,
-			     const struct i40e_rss_filter *rss_filter);
-int i40e_hash_filter_flush(struct i40e_pf *pf);
+			     const struct i40e_rte_flow_rss_conf *rss_conf);
 
 extern const uint8_t i40e_rss_key_default[I40E_RSS_KEY_LEN];
 
drivers/net/intel/i40e/meson.build +1 -0
index 9cff46b5e6..bb15d10f95 100644
@@ -28,6 +28,7 @@ sources += files(
         'i40e_flow_ethertype.c',
         'i40e_flow_fdir.c',
         'i40e_flow_tunnel.c',
+        'i40e_flow_hash.c',
         'i40e_tm.c',
         'i40e_hash.c',
         'i40e_vf_representor.c',
Signature · 1 line
2.52.0
#30 lore raw
In reply to Anatoly Burakov
Quoted 120 lines · On Thu, 20 Aug 2026 15:00:45 +0100 Anatoly Burakov <[email protected]>…

Most rte_flow parsers in DPDK suffer from huge implementation complexity because even though 99% of what people use rte_flow parsers for is parsing protocol graphs, no parser is written explicitly as a graph. This patchset attempts to suggest a viable model to build rte_flow parsers as graphs, by offering a lightweight header only library to build rte_flow parsering graphs without too much boilerplate and complexity.

Most of the patchset is about Intel drivers, but they are meant as reimplementations as well as examples for the rest of the community to assess how to build parsers using this new infrastructure. I expect the first two patches will be of most interest to non-Intel reviewers, as they deal with building two reusable parser architecture pieces.

The first piece is a new flow graph helper in ethdev. Its purpose is deliberately narrow: it targets the protocol-graph part of rte_flow pattern parsing, where drivers walk packet headers and validate legal item sequences and parameters. That does not cover all possible rte_flow features, especially more exotic flow items, but it does cover a large and widely shared part of what existing drivers need to do. Or, to put it in other words, the only flow items this infrastructure *doesn't* cover is things that do not lend themselves well to be parsed as a graph of protocol headers (e.g. conntrack items). Everything else should be covered or cover-able. In practice, just about all drivers will benefit from graph parsing as all but one of them implement only the protocol stack parts, which are the ones targeted by the graph helper.

The second piece is a reusable flow engine framework for Intel Ethernet drivers. This is kept Intel-local because I do not feel it is even appropriate to define such a framework for all drivers to use in the first place. Even so, the intent is to establish a cleaner parser architecture with a defined interaction model, explicit memory ownership rules, and engine definitions that do not block secondary-process-safe usage. It is my hope that this would serve as a model for other drivers to follow, expand on, rework, and improve, so that maybe down the line we *might* have a common rte_flow infrastructure for drivers to use.

Most of the rest of the series is parser reimplementation, but that is mainly the vehicle for demonstrating and validating those two pieces. ixgbe and i40e are wired into the new common parsing path, and their existing parsers are migrated incrementally to the graph-based model. Besides reducing ad hoc parser code, this also makes validation more explicit and more consistent with the actual install path. In a few places that means invalid inputs that were previously ignored, deferred, or interpreted loosely are now rejected earlier and more strictly, without any increase in code complexity (in fact, with marked *decrease* of it!).

For now, there is a hack in

RFC -> v1:
- Fixed a lot of bugs, concurrency issues, and init/uninit sequence problems in Intel common flow engine implementation
- Fixed a few copypaste errors in various engines
- Improved comment documentation and flow engine docs

Anatoly Burakov (21):
ethdev: add flow graph API net/intel/common: add flow engines infrastructure net/intel/common: add utility functions net/ixgbe: add support for common flow parsing net/ixgbe: reimplement ethertype parser net/ixgbe: reimplement syn parser net/ixgbe: reimplement L2 tunnel parser net/ixgbe: reimplement ntuple parser net/ixgbe: reimplement security parser net/ixgbe: reimplement FDIR parser net/ixgbe: reimplement hash parser net/i40e: add support for common flow parsing net/i40e: reimplement ethertype parser net/i40e: reimplement FDIR parser net/i40e: reimplement tunnel QinQ parser net/i40e: reimplement VXLAN parser net/i40e: reimplement NVGRE parser net/i40e: reimplement MPLS parser net/i40e: reimplement gtp parser net/i40e: reimplement L4 cloud parser net/i40e: reimplement hash parser

 doc/guides/prog_guide/ethdev/flow_graph.rst   |  748 +++
 doc/guides/prog_guide/ethdev/index.rst        |    1 +
 doc/guides/rel_notes/release_26_11.rst        |    7 +
 drivers/net/intel/common/flow_engine.h        | 1373 ++++++
 drivers/net/intel/common/flow_util.h          |  183 +
 drivers/net/intel/i40e/i40e_ethdev.c          |   69 +-
 drivers/net/intel/i40e/i40e_ethdev.h          |   66 +-
 drivers/net/intel/i40e/i40e_fdir.c            |   78 +-
 drivers/net/intel/i40e/i40e_flow.c            | 4181 +----------------
 drivers/net/intel/i40e/i40e_flow.h            |   30 +
 drivers/net/intel/i40e/i40e_flow_ethertype.c  |  250 +
 drivers/net/intel/i40e/i40e_flow_fdir.c       | 1834 ++++++++
 drivers/net/intel/i40e/i40e_flow_hash.c       | 1298 +++++
 drivers/net/intel/i40e/i40e_flow_tunnel.c     | 1504 ++++++
 drivers/net/intel/i40e/i40e_hash.c            | 1037 +---
 drivers/net/intel/i40e/i40e_hash.h            |    9 +-
 drivers/net/intel/i40e/meson.build            |    4 +
 drivers/net/intel/i40e/rte_pmd_i40e.c         |    3 +-
 drivers/net/intel/ixgbe/ixgbe_ethdev.c        |   43 +-
 drivers/net/intel/ixgbe/ixgbe_ethdev.h        |   17 +-
 drivers/net/intel/ixgbe/ixgbe_fdir.c          |    1 -
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 3208 +------------
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   27 +
 .../net/intel/ixgbe/ixgbe_flow_ethertype.c    |  245 +
 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c     | 1553 ++++++
 drivers/net/intel/ixgbe/ixgbe_flow_hash.c     |  178 +
 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c    |  230 +
 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c   |  493 ++
 drivers/net/intel/ixgbe/ixgbe_flow_security.c |  320 ++
 drivers/net/intel/ixgbe/ixgbe_flow_syn.c      |  283 ++
 drivers/net/intel/ixgbe/ixgbe_ipsec.c         |   54 +-
 drivers/net/intel/ixgbe/ixgbe_ipsec.h         |    4 +-
 drivers/net/intel/ixgbe/meson.build           |    7 +
 lib/ethdev/meson.build                        |    1 +
 lib/ethdev/rte_flow_graph.h                   |  532 +++
 35 files changed, 11289 insertions(+), 8582 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/flow_graph.rst
 create mode 100644 drivers/net/intel/common/flow_engine.h
 create mode 100644 drivers/net/intel/common/flow_util.h
 create mode 100644 drivers/net/intel/i40e/i40e_flow.h
 create mode 100644 drivers/net/intel/i40e/i40e_flow_ethertype.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_fdir.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_hash.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_tunnel.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow.h
Quoted 9 lines
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ethertype.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_hash.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_security.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_syn.c
 create mode 100644 lib/ethdev/rte_flow_graph.h

It doesn't build:

Best taken up in next-net-intel tree.

AI had some insights.

Review of "[PATCH v1 00/21] Intel flow parser rework" (bundle 2069) Anatoly Burakov <[email protected]>

Applied on top of DPDK main (c1a46b9). Patches 01-08 and 12-20 apply cleanly; 09, 10, 11 and 21 need the prerequisite series noted in the "Depends-on: series-39013 (I40E refactors)" tag, so those four were reviewed from the diffs only.

Patch 01: ethdev: add flow graph API
-----------------------------------

Error: RTE_FLOW_GRAPH_LOG assumes a logtype variable that is not a DPDK-wide convention.

  +#ifdef RTE_COMPONENT_NAME
  +extern int RTE_CONCAT(RTE_COMPONENT_NAME, _logtype_driver);

RTE_COMPONENT_NAME is defined by both drivers/meson.build (line 278) and lib/meson.build (line 241), so in an in-tree build the #ifdef branch is always taken and the #else branch (rte_eth_dev_logtype) is dead. That means every component that includes this header must happen to define <name>_logtype_driver or it fails to link. That holds for the Intel PMDs but is not a project-wide guarantee -- only 67 of the net drivers define such a symbol -- and it is certainly not true for lib/ethdev itself.

This pattern was borrowed from drivers/net/intel/common/log.h, where it is reasonable because the header is documented as Intel-driver-only. A lib/ethdev driver-SDK header should not carry that assumption. Suggest either always using rte_eth_dev_logtype (rte_ethdev.h is already pulled in via rte_flow.h, so the symbol is visible), or passing a logtype into rte_flow_graph_parse().

Error: reserved identifiers. The internal helpers are named __flow_graph_node_check_constraint, __flow_graph_node_is_expected, __flow_graph_node_is_ignored, __flow_graph_get_node_index, __flow_graph_node_is_valid, __flow_graph_find_next_node and __flow_graph_visit_node. Identifiers beginning with two underscores are reserved to the implementation in all scopes (C11 7.1.3). These are static inline in an installed header, so they are injected into every driver translation unit. Use a single leading underscore-free prefix instead, e.g. rte_flow_graph_node_check_constraint(), or drop them behind a naming scheme the project already uses.

Warning: the ALLOW_EXPERIMENTAL_API conditional makes the header behave differently depending on build flags.

+#ifdef ALLOW_EXPERIMENTAL_API
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_NAME_PTR, ...

The 20-line comment above it already concedes this is a workaround ("a hack of monumental proportions", "hopefully we'll come up with a proper solution in one of the next versions of this patchset"). As written, item names silently become "UNKNOWN" in one of the three configurations chkincs builds, so the debug messages this whole function exists to produce are configuration-dependent. Either fix chkincs to skip experimental-disabled builds for driver_sdk headers, or drop the name lookup and log the numeric item type, which needs no experimental API at all.

Warning: RTE_FLOW_NODE_EDGE_END is defined as ((size_t)~0U), which is 0xFFFFFFFF on LP64, not SIZE_MAX. It works because producers and consumers both use the macro, but it reads like a bug and will trip anyone who writes SIZE_MAX in a driver edge table. Use ((size_t)-1) or SIZE_MAX.

Info: struct rte_flow_graph holds non-const pointers:

  +struct rte_flow_graph {
  +	struct rte_flow_graph_node *nodes;
  +	struct rte_flow_graph_edge *edges;

Everything in the traversal path treats the graph as read-only, and the design notes in patch 02 explicitly require engine definitions to live in read-only memory. Making both members const would let drivers declare the tables const without casting.

Info: struct rte_flow_graph_node declares two members const (type, constraints) and two non-const. A struct with const members cannot be assigned or memcpy'd as a whole; the mix is arbitrary. Either const the whole struct at the point of definition or none of the members.

Info: the guide is added under doc/guides/prog_guide/ethdev/, which is the application programmer's guide, but the header's own @file block says "This is an internal API for PMD drivers only. Applications must not use it." Consider placing it where the other driver-facing material lives.

Info: doc/guides/prog_guide/ethdev/flow_graph.rst says "It is not recommended to create loops in the graph, as these loops will be unbounded." Traversal is driven by the pattern array, not by the graph, so a cycle in the edge tables is bounded by pattern length. The statement is misleading as written.

Patch 02: net/intel/common: add flow engines infrastructure
-----------------------------------------------------------

Error: ci_flow_alloc() converts an engine's "out of capacity" signal into a successful generic allocation.

  +	if (engine->ops->flow_alloc != NULL)
  +		flow = engine->ops->flow_alloc(engine, engine_conf->dev_data, priv);
  +	if (flow == NULL) {
  +		flow = (struct ci_flow *)rte_zmalloc(NULL, engine->flow_size, 0);
  +		if (flow != NULL && engine->ops->flow_alloc != NULL)
  +			fallback = true;

The only engine in the series with a custom allocator is i40e fdir (patch 14), whose flow_alloc() returns NULL for two distinct reasons:
the hardware table is full (fdir_actual_cnt >= fdir_space_size) and the flow pool bitmap is exhausted. Both are capacity limits, and both are silently overridden here: the framework hands back a heap flow, ci_flow_create() proceeds to flow_install(), and ci_flow_free() then takes the rte_free() path so the engine never learns the flow existed. The pool index accounting in i40e_fdir_flow_free() is bypassed entirely.

A NULL from flow_alloc() should be treated as "this engine cannot take this flow" and move on to the next engine, or be an outright failure. If a fallback is genuinely wanted, the engine needs a way to distinguish "allocation failed, try the generic path" from "no capacity".

Warning: ci_flow_validate() frees the trial flow with plain free() after running the full parse chain including ctx_to_flow():

  +		flow = (struct ci_flow *)calloc(1, engine_ref.engine->flow_size);
  ...
  +		ret = ci_flow_parse(engine_conf, engine_ref.engine, attr, pattern,
  +				actions, flow, error);
  +		free(flow);

The header's own design notes say "Engines may still allocate auxiliary data (for per-engine private state or pointers stored inside flows) ... [which] should follow engine_init/engine_uninit or flow lifetime as appropriate." Any engine that honours that contract by allocating in ctx_to_flow() will leak on every rte_flow_validate() call. No engine in this series does so yet, so this is latent, but the framework contract and the framework implementation disagree. Either document that ctx_to_flow() must not allocate, or give the validate path a teardown hook.

Warning: ci_flow_flush() frees flows whose uninstall failed.

  +		/* ignore failures */
  +		ci_flow_uninstall(engine_ref, flow, error);
  +
  +		TAILQ_REMOVE(&engine_conf->flows, flow, node);
  +		ci_flow_free(engine_ref, flow);

If flow_uninstall() fails the rule is still programmed in hardware, but the software object is destroyed and removed from the list, so there is no longer any way to retry or to account for it. Worth at least logging the failure and returning non-zero from ci_flow_flush() rather than unconditionally returning 0. The same &error is also reused across iterations, so only the last failure survives.

Warning: ci_flow_engine_conf_reset() takes the write lock and is documented "thread-safe", but ci_flow_engine_conf_init() calls rte_rwlock_init() on the same lock without holding it, and is documented "thread-unsafe". The pairing is confusing. Since both are primary-process-only setup/teardown per the design notes, dropping the lock from reset() and documenting both as serialised by the caller would be clearer than the current asymmetry.

Info: ci_flow_alloc() uses rte_zmalloc(NULL, ...) for the flow object. Flows are stored in the shared per-device flow list and the whole design goal is secondary-process compatibility, so rte_zmalloc_socket() with the device's socket_id would keep the allocation NUMA-local to the port. Same for the priv_size allocation in ci_flow_engine_init() and the pool/bitmap allocations in i40e fdir engine_init.

Info: struct ci_flow_engine_list is a fixed 64-entry array (CI_FLOW_ENGINE_MAX), so every driver pays 512 bytes for what is currently four or five engines. A pointer plus count would be no harder to use with the FOREACH macro and would drop the NULL- termination requirement.

Patch 03: net/intel/common: add utility functions
--------------------------------------------------

Info: header guard comment does not match the guard.

+#ifndef _COMMON_INTEL_FLOW_UTIL_H_
...
+#endif /* _INTEL_COMMON_FLOW_UTIL_H_ */

Patch 04: net/ixgbe: add support for common flow parsing
---------------------------------------------------------

Warning: ixgbe_flow_destroy() misreports a failed uninstall as "Flow not found".

+ ret = ci_flow_destroy(&adapter->flow_engine_conf, flow, error);
+ if (ret == 0)
+ return 0;
+
+ /* fall back to legacy engines */

ci_flow_destroy() returns non-zero for two unrelated cases: the handle does not belong to the new engine (correct: fall through to legacy), and the handle does belong to it but flow_uninstall() failed. In the second case control falls into the legacy path, the ownership scan over adapter->flow_list does not find the handle, and the caller gets EINVAL "Flow not found for this port" while the rule is still programmed and still on the engine's flow list. ci_flow_destroy() needs to distinguish "not mine" from "mine, and it failed" -- e.g. return -ENOENT for the former -- so the driver can propagate the real error.

Warning: the same function reads through the handle before ownership is established:

  +	struct rte_flow *pmd_flow = flow;
  +	enum rte_filter_type filter_type = pmd_flow->filter_type;

struct rte_flow embeds struct ci_flow as its first member, so a legacy flow is a valid ci_flow, but the reverse does not hold: a flow created by the new engine is sized by engine->flow_size and the bytes at that offset are engine-private state. The value is unused when ci_flow_destroy() succeeds, so this is not currently exploitable, but it is a type-punned read on every destroy. Move the initialisation below the ci_flow_destroy() early return.

Info: ixgbe_flow_create() falls through to the legacy engines even when ci_flow_create() failed for a non-recoverable reason (ENOMEM from ci_flow_alloc(), which does "goto unlock" precisely because "this is a serious error so don't continue"). Distinguishing ENOTSUP from ENOMEM at the driver boundary would preserve that intent. The stale rte_flow_error left behind by the failed ci_flow_create() call is also overwritten only if the legacy path itself sets one.

Patch 14: net/i40e: reimplement FDIR parser
--------------------------------------------

Error: i40e_fdir_flow_uninstall() marks its error parameter __rte_unused and then uses it.

+i40e_fdir_flow_uninstall(struct ci_flow *flow, struct rte_flow_error *error __rte_unused)
...
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "Failed to delete fdir filter.");

__rte_unused is __attribute__((unused)), which suppresses the warning rather than forbidding use, so this compiles -- but the annotation is wrong and will mislead the next reader. Drop it.

Info: i40e_fdir_flow_engine_uninit() does rte_free(priv->bmp), which relies on rte_bitmap_init() returning its mem argument unchanged. That is true today but it is an implementation detail of rte_bitmap.h. Storing bmp_mem alongside bmp in
struct i40e_fdir_engine_priv, and freeing that, would not depend on it.

General notes
-------------

The cover letter's "Depends-on: series-39013" is on patch 01 only. Since 09/10/11 and 21 are the patches that actually fail to apply without it, it would help to repeat the tag on the cover letter so it shows up in patchwork for the whole series.

Overlap with the testpmd flow-parser rework
--------------------------------------------

No overlap. The two efforts sit on opposite sides of the rte_flow API:

- The testpmd work is about app/test-pmd/cmdline_flow.c, the CLI grammar that turns "flow create 0 ingress pattern eth / ipv4 / end actions queue index 3 / end" into arrays of struct rte_flow_item and struct rte_flow_action. That is a text parser, which is why lex/yacc is a natural fit.

- This series is about what a PMD does with those arrays once rte_flow_create() hands them over. rte_flow_graph_parse() walks an already-constructed rte_flow_item array against a driver's protocol graph and extracts hardware register fields. There is no text anywhere in it.

Concretely, no patch in the series touches app/ at all -- the 21 patches are confined to lib/ethdev, doc/, and drivers/net/intel. The only shared surface is rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_NAME_PTR), which patch 01 uses for debug strings and cmdline_flow.c uses for token names; if the yacc/lex rework changes rte_flow_conv semantics both would need to follow, but that is a soft coupling, not a conflict. A merge conflict between the two is not possible on the current file sets.

Tree routing
------------

Mostly agreed, with one caveat.

Patches 02-21 are entirely under drivers/net/intel (common, ixgbe, i40e) and belong in dpdk-next-net-intel. The Depends-on series is also i40e, which reinforces that.

Patch 01 is the exception: it adds lib/ethdev/rte_flow_graph.h, lib/ethdev/meson.build, a prog_guide chapter and a release note. MAINTAINERS covers it under "F: lib/ethdev/rte_flow*", i.e. the Flow API section, so it wants an ack from the flow API and ethdev maintainers before it moves, even if it ultimately travels with the rest of the series through next-net-intel to keep the series bisectable. Given that patch 01 is the one with the design questions above (logtype coupling, reserved identifiers, the ALLOW_EXPERIMENTAL_API workaround), it is probably worth asking for it to be settled on-list before the driver patches are queued.

#31 lore raw

v2 cover Building a better rte_flow parser

In reply to Anatoly Burakov

Most rte_flow parsers in DPDK suffer from huge implementation complexity because even though 99% of what people use rte_flow parsers for is parsing protocol graphs, no parser is written explicitly as a graph. This patchset attempts to suggest a viable model to build rte_flow parsers as graphs, by offering a lightweight header only library to build rte_flow parsering graphs without too much boilerplate and complexity.

Most of the patchset is about Intel drivers, but they are meant as reimplementations as well as examples for the rest of the community to assess how to build parsers using this new infrastructure. I expect the first two patches will be of most interest to non-Intel reviewers, as they deal with building two reusable parser architecture pieces.

The first piece is a new flow graph helper in ethdev. Its purpose is deliberately narrow: it targets the protocol-graph part of rte_flow pattern parsing, where drivers walk packet headers and validate legal item sequences and parameters. That does not cover all possible rte_flow features, especially more exotic flow items, but it does cover a large and widely shared part of what existing drivers need to do. Or, to put it in other words, the only flow items this infrastructure *doesn't* cover is things that do not lend themselves well to be parsed as a graph of protocol headers (e.g. conntrack items). Everything else should be covered or cover-able. In practice, just about all drivers will benefit from graph parsing as all but one of them implement only the protocol stack parts, which are the ones targeted by the graph helper.

The second piece is a reusable flow engine framework for Intel Ethernet drivers. This is kept Intel-local because I do not feel it is even appropriate to define such a framework for all drivers to use in the first place. Even so, the intent is to establish a cleaner parser architecture with a defined interaction model, explicit memory ownership rules, locking, initialization sequence, implementations of rte_flow API entry points, flow replay and memory cleanup, and engine definitions that do not block secondary-process-safe usage. It is my hope that this would serve as a model for other drivers to follow, expand on, rework, and improve, so that maybe down the line we *might* have a common rte_flow infrastructure for drivers to use.

Most of the rest of the series is parser reimplementation, but that is mainly the vehicle for demonstrating and validating those two pieces. ixgbe and i40e are wired into the new common parsing path, and their existing parsers are migrated incrementally to the graph-based model. Besides reducing ad hoc parser code, this also makes validation more explicit and more consistent. In a few places that means invalid inputs that were previously ignored, deferred, or interpreted loosely are now rejected earlier and more strictly, without any increase in code complexity (in fact, with marked *decrease* of it!).

v1 -> v2:
- Renamed rte_flow_graph to flow_graph
- Addressed some comments from Stephen's AI review
- Removed the rte_flow_conv hack on account of Stephen's upcoming patches making the API stable
- Split up "flow_install" into register and install steps, which enabled two new use cases: cleanup of memory without HW deregistration, and flow configuration replay after dev_start
- More aggressive refactoring of all engines on account of new pieces of infrastructure, which allows for reduced complexity of implementation
- Combined i40e tunnel engine patches into one patch
- Added i40e flow director refactoring to cleanly support the new API as well as the legacy PMD-specific API

RFC -> v1:
- Fixed a lot of bugs, concurrency issues, and init/uninit sequence problems in Intel common flow engine implementation
- Fixed a few copypaste errors in various engines
- Improved comment documentation and flow engine docs

Anatoly Burakov (19):
ethdev: add flow graph API net/intel/common: add flow engines infrastructure net/intel/common: add utility functions net/ixgbe: add support for common flow parsing net/ixgbe: reimplement ethertype parser net/ixgbe: reimplement syn parser net/ixgbe: reimplement L2 tunnel parser net/ixgbe: reimplement ntuple parser net/ixgbe: reimplement security parser net/ixgbe: reimplement FDIR parser net/ixgbe: reimplement hash parser net/ixgbe: advertise flow keep capability net/i40e: add support for common flow parsing net/i40e: reimplement ethertype parser net/i40e: refactor FDIR engine infrastructure net/i40e: reimplement FDIR parser net/i40e: reimplement tunnel parsers net/i40e: reimplement hash parser net/i40e: advertise flow keep capability

 doc/guides/prog_guide/ethdev/flow_graph.rst   |  748 +++
 doc/guides/prog_guide/ethdev/index.rst        |    1 +
 doc/guides/rel_notes/release_26_11.rst        |    5 +
 drivers/net/intel/common/flow_engine.h        | 1613 +++++++
 drivers/net/intel/common/flow_util.h          |  183 +
 drivers/net/intel/i40e/i40e_ethdev.c          |  996 +---
 drivers/net/intel/i40e/i40e_ethdev.h          |  261 +-
 drivers/net/intel/i40e/i40e_fdir.c            |  983 ++--
 drivers/net/intel/i40e/i40e_flow.c            | 4185 +----------------
 drivers/net/intel/i40e/i40e_flow.h            |   30 +
 drivers/net/intel/i40e/i40e_flow_ethertype.c  |  348 ++
 drivers/net/intel/i40e/i40e_flow_fdir.c       | 2056 ++++++++
 drivers/net/intel/i40e/i40e_flow_hash.c       | 1441 ++++++
 drivers/net/intel/i40e/i40e_flow_tunnel.c     | 1590 +++++++
 drivers/net/intel/i40e/i40e_hash.c            | 1208 +----
 drivers/net/intel/i40e/i40e_hash.h            |   19 +-
 drivers/net/intel/i40e/meson.build            |    4 +
 drivers/net/intel/i40e/rte_pmd_i40e.c         |    4 +-
 drivers/net/intel/ixgbe/ixgbe_ethdev.c        | 1002 +---
 drivers/net/intel/ixgbe/ixgbe_ethdev.h        |  246 +-
 drivers/net/intel/ixgbe/ixgbe_fdir.c          |  304 +-
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 3213 +------------
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   27 +
 .../net/intel/ixgbe/ixgbe_flow_ethertype.c    |  295 ++
 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c     | 1703 +++++++
 drivers/net/intel/ixgbe/ixgbe_flow_hash.c     |  212 +
 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c    |  317 ++
 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c   |  597 +++
 drivers/net/intel/ixgbe/ixgbe_flow_security.c |  537 +++
 drivers/net/intel/ixgbe/ixgbe_flow_syn.c      |  312 ++
 drivers/net/intel/ixgbe/ixgbe_ipsec.c         |  359 +-
 drivers/net/intel/ixgbe/ixgbe_ipsec.h         |   50 +-
 drivers/net/intel/ixgbe/ixgbe_pf.c            |   49 +-
 drivers/net/intel/ixgbe/ixgbe_rxtx.c          |   48 +-
 drivers/net/intel/ixgbe/meson.build           |    7 +
 lib/ethdev/flow_graph.h                       |  507 ++
 lib/ethdev/meson.build                        |    1 +
 37 files changed, 14102 insertions(+), 11359 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/flow_graph.rst
 create mode 100644 drivers/net/intel/common/flow_engine.h
 create mode 100644 drivers/net/intel/common/flow_util.h
 create mode 100644 drivers/net/intel/i40e/i40e_flow.h
 create mode 100644 drivers/net/intel/i40e/i40e_flow_ethertype.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_fdir.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_hash.c
 create mode 100644 drivers/net/intel/i40e/i40e_flow_tunnel.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow.h
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ethertype.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_hash.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_security.c
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_syn.c
 create mode 100644 lib/ethdev/flow_graph.h
Signature · 1 line
2.52.0
#32 lore raw

PATCH v2 1/19 ethdev: add flow graph API

This commit adds a flow graph parsing API. This is a helper API intended to help ethdev drivers implement rte_flow parsers, as common usages map to graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

---
 doc/guides/prog_guide/ethdev/flow_graph.rst | 748 ++++++++++++++++++++
 doc/guides/prog_guide/ethdev/index.rst      |   1 +
 doc/guides/rel_notes/release_26_11.rst      |   5 +
 lib/ethdev/flow_graph.h                     | 507 +++++++++++++
 lib/ethdev/meson.build                      |   1 +
 5 files changed, 1262 insertions(+)
 create mode 100644 doc/guides/prog_guide/ethdev/flow_graph.rst
 create mode 100644 lib/ethdev/flow_graph.h
doc/guides/prog_guide/ethdev/flow_graph.rst new file +748 -0
new file mode 100644
index 0000000000..87b6b80f1d
@@ -0,0 +1,748 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Intel Corporation
+
+Flow Graph Parser
+=================
+
+Introduction
+------------
+
+The flow graph parser is a helper library for PMD drivers that implements ``rte_flow`` pattern matching.
+It lets a driver declare the protocol sequences it supports as a directed graph of nodes and edges.
+It then validates and extracts fields from an ``rte_flow_item`` pattern in a single traversal.
+
+The library is defined in ``flow_graph.h`` and is header-only.
+
+Scope and Limitations
+~~~~~~~~~~~~~~~~~~~~~
+
+Because the parser is graph-based, it is well suited for matching *protocol stacks*.
+These are sequences of protocol headers such as ``ETH / IPv4 / TCP``.
+Any pattern that can be expressed as "from protocol A, transitions to protocol B or C are allowed" fits naturally into the graph model.
+
+The library is **not** designed to cover every ``rte_flow`` item type.
+Items that do not represent a position in a protocol stack do not have a natural place in a protocol graph.
+This includes conntrack state, meter color, and other metadata items.
+Such items are best handled outside the graph, either before or after the graph parse call.
+
+Defining a Graph
+----------------
+
+A graph consists of three parts:
+
+1. An **enum** that assigns a numeric index to every node.
+2. A **node array** (``struct flow_graph_node[]``) indexed by that enum.
+3. An **edge array** (``struct flow_graph_edge[]``) also indexed by that enum, describing allowed transitions.
+
+These parts are bundled together in a ``struct flow_graph``.
+
+The running example used throughout this guide models the following protocol graph::
+
+   START -> ETH -> [VLAN] -> (IPv4 | IPv6) -> [(TCP | UDP | SCTP)] -> END
+
+Brackets ``[...]`` denote optional items.
+Parentheses ``(...)`` denote a required choice between alternatives.
+The key ideas are:
+
+* ``ETH`` is required after ``START``.
+* After ``ETH``, an optional ``VLAN`` may appear, but the pattern must then see an IP layer.
+* After an IP layer, an optional transport layer may appear; it may be TCP, UDP, or SCTP, after which the pattern reaches ``END``.
+
+Node Enum
+~~~~~~~~~
+
+Every node needs a stable index.
+The first node **must** be at index ``FLOW_GRAPH_NODE_FIRST`` (which is 0).
+This is the *start node*.
+It is used only as a traversal anchor and must not carry callbacks.
+
+.. code-block:: c
+
+   enum example_node_id {
+       EXAMPLE_NODE_START = FLOW_GRAPH_NODE_FIRST,
+       EXAMPLE_NODE_ETH,
+       EXAMPLE_NODE_VLAN,
+       EXAMPLE_NODE_IPV4,
+       EXAMPLE_NODE_IPV6,
+       EXAMPLE_NODE_TCP,
+       EXAMPLE_NODE_UDP,
+       EXAMPLE_NODE_SCTP,
+       EXAMPLE_NODE_END,
+       /* keep last */
+       EXAMPLE_NODE_MAX,
+   };
+
+Node Definitions
+~~~~~~~~~~~~~~~~
+
+Each node maps to one ``rte_flow_item_type``.
+It can also carry a *validate* callback, a *process* callback, and a set of *constraints*.
+The the ``END`` node can also have callbacks to perform end-of-match processing.
+
+A minimal skeleton (callbacks and constraints are added in later sections):
+
+.. code-block:: c
+
+   const struct flow_graph example_graph = {
+       .nodes = (struct flow_graph_node[]){
+           [EXAMPLE_NODE_START] = {
+               .name = "START",
+               /* Start node: no type, no callbacks */
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .name  = "ETH",
+               .type  = RTE_FLOW_ITEM_TYPE_ETH,
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .name  = "VLAN",
+               .type  = RTE_FLOW_ITEM_TYPE_VLAN,
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .name  = "IPV4",
+               .type  = RTE_FLOW_ITEM_TYPE_IPV4,
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .name  = "IPV6",
+               .type  = RTE_FLOW_ITEM_TYPE_IPV6,
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .name  = "TCP",
+               .type  = RTE_FLOW_ITEM_TYPE_TCP,
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .name  = "UDP",
+               .type  = RTE_FLOW_ITEM_TYPE_UDP,
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .name  = "SCTP",
+               .type  = RTE_FLOW_ITEM_TYPE_SCTP,
+           },
+           [EXAMPLE_NODE_END] = {
+               .name  = "END",
+               .type  = RTE_FLOW_ITEM_TYPE_END,
+           },
+       },
+   };
+
+Edge Definitions
+~~~~~~~~~~~~~~~~
+
+Edges express which nodes may follow the current one.
+Every edge list is terminated by the ``FLOW_GRAPH_NODE_EDGE_END`` sentinel.
+All non-``END`` nodes **must** have an edge list.
+The ``END`` node itself does not need one.
+
+.. code-block:: c
+
+   const struct flow_graph example_graph = {
+       .nodes = (struct flow_graph_node[]){
+           /* ... same nodes as above ... */
+       },
+       .edges = (struct flow_graph_edge[]){
+           [EXAMPLE_NODE_START] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_ETH,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_VLAN,
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+       },
+   };
+
+Reading the edges back:
+
+* From ``START``, the parser can only reach ``ETH``, which makes ``ETH`` required.
+* From ``ETH``, the parser can reach ``VLAN``, ``IPV4``, or ``IPV6``, which makes ``VLAN`` optional.
+* From ``IPV4`` or ``IPV6``, the parser can reach ``TCP``, ``UDP``, ``SCTP``, or ``END``, which makes the transport layer optional.
+
+Assembling the Graph
+~~~~~~~~~~~~~~~~~~~~
+
+With nodes and edges defined inline, assembling the graph is just a matter of
+combining the two arrays into a single compound literal:
+
+.. code-block:: c
+
+   const struct flow_graph example_graph = {
+       .nodes = (struct flow_graph_node[]){
+           [EXAMPLE_NODE_START] = {
+               .name = "START"
+           },
+           [EXAMPLE_NODE_ETH]   = {
+               .name = "ETH",
+               .type = RTE_FLOW_ITEM_TYPE_ETH
+           },
+           /* ... remaining nodes ... */
+           [EXAMPLE_NODE_END]   = {
+               .name = "END",
+               .type = RTE_FLOW_ITEM_TYPE_END
+           },
+       },
+       .edges = (struct flow_graph_edge[]){
+           [EXAMPLE_NODE_START] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_ETH,
+                   FLOW_GRAPH_NODE_EDGE_END
+               }
+           },
+           /* ... remaining edges ... */
+       },
+   };
+
+Callbacks
+---------
+
+The graph calls up to two callbacks on every visited node: *validate* and *process*.
+
+Both callbacks share the same return convention.
+On success they must return ``0``.
+On failure they must call ``rte_flow_error_set`` to record a descriptive error and return its result.
+
+Validate Callback
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+   typedef int (*flow_graph_node_validate_fn)(
+       const void *ctx,
+       const struct rte_flow_item *item,
+       struct rte_flow_error *error);
+
+This callback receives a **read-only** context pointer.
+The canonical intent is that it should check whether the item's spec, mask, and last values are acceptable for the driver.
+On failure it returns the result of ``rte_flow_error_set`` (see the return convention above).
+
+It is recommended to use this callback for **all checks that can reject a rule**.
+This includes unsupported mask bits, conflicting field combinations, hardware limitations, and other applicable criteria.
+
+Process Callback
+~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+   typedef int (*flow_graph_node_process_fn)(
+       void *ctx,
+       const struct rte_flow_item *item,
+       struct rte_flow_error *error);
+
+This callback receives a **mutable** context pointer.
+The canonical expectation is that it should extract the fields needed for hardware programming.
+It should then store extracted data in the driver's context structure.
+On the rare failure path it returns the result of ``rte_flow_error_set`` (see the return convention above).
+
+It is recommended to use this callback for the **happy path**.
+For example, it can copy addresses, ports, and protocol IDs into the driver context so they can be programmed later.
+
+Defining a Context Structure
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The opaque ``ctx`` pointer passed to every callback is driver-defined.
+A typical context accumulates the parsed protocol fields:
+
+.. code-block:: c
+
+   struct example_parsed_flow {
+       /* L2 */
+       struct rte_ether_addr dst_mac;
+       bool has_vlan;
+       uint16_t vlan_tci;
+
+       /* L3 */
+       bool is_ipv6;
+       rte_be32_t ipv4_src;
+       rte_be32_t ipv4_dst;
+       uint8_t ipv6_src[16];
+       uint8_t ipv6_dst[16];
+
+       /* L4 */
+       enum rte_flow_item_type l4_proto;
+       rte_be16_t src_port;
+       rte_be16_t dst_port;
+   };
+
+These fields are meant to reflect the structure used by the driver to programming hardware with.
+
+Callback Example
+~~~~~~~~~~~~~~~~
+
+Below is a validate/process pair for the IPv4 node.
+The validate callback rejects unsupported mask bits.
+The process callback copies addresses into the context:
+
+.. code-block:: c
+
+   static int
+   example_validate_ipv4(const void *ctx __rte_unused,
+                         const struct rte_flow_item *item,
+                         struct rte_flow_error *error)
+   {
+       const struct rte_flow_item_ipv4 *mask = item->mask;
+
+       if (mask->hdr.version_ihl ||
+           mask->hdr.type_of_service ||
+           mask->hdr.total_length ||
+           mask->hdr.packet_id ||
+           mask->hdr.fragment_offset ||
+           mask->hdr.time_to_live ||
+           mask->hdr.next_proto_id ||
+           mask->hdr.hdr_checksum) {
+           return rte_flow_error_set(error, EINVAL,
+                   RTE_FLOW_ERROR_TYPE_ITEM, item,
+                   "Only src/dst addresses supported");
+       }
+       return 0;
+   }
+
+   static int
+   example_process_ipv4(void *ctx,
+                        const struct rte_flow_item *item,
+                        struct rte_flow_error *error __rte_unused)
+   {
+       struct example_parsed_flow *parsed = ctx;
+       const struct rte_flow_item_ipv4 *spec = item->spec;
+
+       parsed->is_ipv6 = false;
+       if (spec != NULL) {
+           parsed->ipv4_src = spec->hdr.src_addr;
+           parsed->ipv4_dst = spec->hdr.dst_addr;
+       }
+       return 0;
+   }
+
+Add the callbacks to the node definition:
+
+.. code-block:: c
+
+   [EXAMPLE_NODE_IPV4] = {
+       .name      = "IPV4",
+       .type      = RTE_FLOW_ITEM_TYPE_IPV4,
+       .validate  = example_validate_ipv4,
+       .process   = example_process_ipv4,
+   },
+
+Node Constraints
+----------------
+
+Many nodes share common requirements about which combination of ``spec``, ``mask``, and ``last`` pointers an item must carry.
+Instead of checking these in every validate callback, they can be declared via the ``constraints`` field.
+The field uses ``flow_graph_node_expect`` flags.
+
+Available constraint flags (may be ORed together):
+
+``FLOW_GRAPH_NODE_EXPECT_EMPTY``
+   The item must have ``spec == NULL``, ``mask == NULL``, and
+   ``last == NULL``.
+
+``FLOW_GRAPH_NODE_EXPECT_SPEC``
+   ``spec`` is required; ``mask`` and ``last`` must be NULL.
+
+``FLOW_GRAPH_NODE_EXPECT_MASK``
+   ``mask`` is required; ``spec`` and ``last`` must be NULL.
+
+``FLOW_GRAPH_NODE_EXPECT_SPEC_MASK``
+   Both ``spec`` and ``mask`` are required; ``last`` must be NULL.
+
+``FLOW_GRAPH_NODE_EXPECT_RANGE``
+   All three (``spec``, ``mask``, ``last``) are required.
+
+``FLOW_GRAPH_NODE_EXPECT_NOT_RANGE``
+   ``last`` must be NULL (``spec`` and ``mask`` are unconstrained).
+
+Multiple flags can be ORed together.
+The item is accepted if **any one** of the flagged constraints is satisfied.
+
+For example, an IPv4 node that accepts either a mask-only item or a spec+mask item:
+
+.. code-block:: c
+
+   [EXAMPLE_NODE_IPV4] = {
+       .name        = "IPV4",
+       .type        = RTE_FLOW_ITEM_TYPE_IPV4,
+       .validate    = example_validate_ipv4,
+       .process     = example_process_ipv4,
+       .constraints = FLOW_GRAPH_NODE_EXPECT_MASK |
+                      FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+   },
+
+An Ethernet node that may appear empty (no spec/mask) or with spec+mask:
+
+.. code-block:: c
+
+   [EXAMPLE_NODE_ETH] = {
+       .name        = "ETH",
+       .type        = RTE_FLOW_ITEM_TYPE_ETH,
+       .validate    = example_validate_eth,
+       .process     = example_process_eth,
+       .constraints = FLOW_GRAPH_NODE_EXPECT_EMPTY |
+                      FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+   },
+
+Constraints are checked **before** the validate callback is invoked.
+
+Ignoring Item Types
+~~~~~~~~~~~~~~~~~~~
+
+The ``ignore_nodes`` field on ``struct flow_graph`` is an optional complement to node constraints.
+When the pattern may contain item types that are irrelevant to the driver, list them in ``ignore_nodes``.
+For example, metadata items like ``RTE_FLOW_ITEM_TYPE_MARK`` do not represent a protocol header.
+The parser skips ignored items silently without advancing the current graph position:
+
+.. code-block:: c
+
+   const struct flow_graph example_graph = {
+       /* ... nodes and edges ... */
+       .ignore_nodes = (const enum rte_flow_item_type[]){
+           RTE_FLOW_ITEM_TYPE_MARK,
+           RTE_FLOW_ITEM_TYPE_END,
+       },
+   };
+
+``RTE_FLOW_ITEM_TYPE_VOID`` is always ignored regardless of this list.
+Omit ``ignore_nodes`` entirely when no additional item types need to be skipped.
+
+Calling the Parser
+------------------
+
+``flow_graph_parse`` walks the pattern against the graph:
+
+.. code-block:: c
+
+   int
+   flow_graph_parse(const struct flow_graph *graph,
+                        const struct rte_flow_item *pattern,
+                        struct rte_flow_error *error,
+                        void *ctx);
+
+A typical call site looks like this:
+
+.. code-block:: c
+
+   struct example_parsed_flow parsed;
+   int ret;
+
+   memset(&parsed, 0, sizeof(parsed));
+
+   ret = flow_graph_parse(&example_graph, pattern, error, &parsed);
+   if (ret != 0)
+       return ret;
+
+   /* 'parsed' now contains the extracted protocol fields */
+
+The function returns success or failure, with ``error`` populated.
+
+Error conditions:
+
+* **Graph is NULL** — for example, when the graph pointer itself is not provided.
+* **Pattern is NULL**.
+* **Unsupported transition** — when an item type has no matching edge from the current node.
+    This is the primary way the graph rejects unsupported protocol sequences.
+* **Constraint failure** — when the spec, mask, and last combination does not satisfy the node's declared constraints.
+* **Validate callback failure** — when driver-specific validation rejects the item.
+* **Process callback failure** — when driver-specific extraction path fails.
+
+.. warning::
+
+    Malformed graph tables (for example invalid node indices, missing sentinels,
+    or otherwise inconsistent driver-defined graph structures) are considered to be a driver implementation bug.
+    Graphs are trusted by default: driver-owned graph structures are expected to be valid and are not fully validated.
+
+The traversal processes items in order, skipping ignored types.
+After the last non-``END`` item, the parser looks for an ``END`` node reachable from the current position.
+It then visits that node and runs its callbacks, if any.
+This means drivers can attach a process callback to the ``END`` node for post-traversal finalization.
+
+Putting It All Together
+-----------------------
+
+The complete graph definition with callbacks and constraints:
+
+.. code-block:: c
+
+   const struct flow_graph example_graph = {
+       .nodes = (struct flow_graph_node[]){
+           [EXAMPLE_NODE_START] = {
+               .name = "START",
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .name        = "ETH",
+               .type        = RTE_FLOW_ITEM_TYPE_ETH,
+               .validate    = example_validate_eth,
+               .process     = example_process_eth,
+               .constraints = FLOW_GRAPH_NODE_EXPECT_EMPTY
+                            | FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .name        = "VLAN",
+               .type        = RTE_FLOW_ITEM_TYPE_VLAN,
+               .process     = example_process_vlan,
+               .constraints = FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .name        = "IPV4",
+               .type        = RTE_FLOW_ITEM_TYPE_IPV4,
+               .validate    = example_validate_ipv4,
+               .process     = example_process_ipv4,
+               .constraints = FLOW_GRAPH_NODE_EXPECT_MASK
+                            | FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .name        = "IPV6",
+               .type        = RTE_FLOW_ITEM_TYPE_IPV6,
+               .validate    = example_validate_ipv6,
+               .process     = example_process_ipv6,
+               .constraints = FLOW_GRAPH_NODE_EXPECT_MASK
+                            | FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .name        = "TCP",
+               .type        = RTE_FLOW_ITEM_TYPE_TCP,
+               .validate    = example_validate_tcp,
+               .process     = example_process_tcp,
+               .constraints = FLOW_GRAPH_NODE_EXPECT_MASK
+                            | FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .name        = "UDP",
+               .type        = RTE_FLOW_ITEM_TYPE_UDP,
+               .validate    = example_validate_udp,
+               .process     = example_process_udp,
+               .constraints = FLOW_GRAPH_NODE_EXPECT_MASK
+                            | FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .name        = "SCTP",
+               .type        = RTE_FLOW_ITEM_TYPE_SCTP,
+               .process     = example_process_sctp,
+               .constraints = FLOW_GRAPH_NODE_EXPECT_EMPTY
+                            | FLOW_GRAPH_NODE_EXPECT_MASK
+                            | FLOW_GRAPH_NODE_EXPECT_SPEC_MASK,
+           },
+           [EXAMPLE_NODE_END] = {
+               .name = "END",
+               .type = RTE_FLOW_ITEM_TYPE_END,
+           },
+       },
+       .edges = (struct flow_graph_edge[]){
+           [EXAMPLE_NODE_START] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_ETH,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_ETH] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_VLAN,
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_VLAN] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_IPV4,
+                   EXAMPLE_NODE_IPV6,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV4] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_IPV6] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_TCP,
+                   EXAMPLE_NODE_UDP,
+                   EXAMPLE_NODE_SCTP,
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_TCP] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_UDP] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [EXAMPLE_NODE_SCTP] = {
+               .next = (size_t[]){
+                   EXAMPLE_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+       },
+   };
+
+
+Tunnel Graphs and Repeated Item Types
+-------------------------------------
+
+Tunneled patterns often repeat the same ``rte_flow_item_type`` in outer and inner headers.
+A simple representative example is a TCP-IPv4 over GTP-U pattern::
+
+   ETH -> IPV4 -> UDP -> GTPU -> IPV4 -> TCP
+
+The graph library supports this naturally.
+Multiple nodes may use the same ``type`` value, as long as they are distinct nodes in the graph and reached through different edges.
+
+For tunnel parsing, the recommended style is to model repeated protocol types as separate inner/outer nodes, for example ``OUTER_IPV4`` and ``INNER_IPV4``.
+This makes the graph intent explicit, keeps callback logic clear, and avoids unexpected graph paths due to loops.
+
+.. code-block:: c
+
+   enum tunnel_node_id {
+       TUNNEL_NODE_START = FLOW_GRAPH_NODE_FIRST,
+       TUNNEL_NODE_ETH,
+       TUNNEL_NODE_OUTER_IPV4,
+       TUNNEL_NODE_TCP,
+       TUNNEL_NODE_UDP,
+       TUNNEL_NODE_GTPU,
+       TUNNEL_NODE_INNER_IPV4,
+       TUNNEL_NODE_END,
+       TUNNEL_NODE_MAX,
+   };
+
+   const struct flow_graph tunnel_graph = {
+       .nodes = (struct flow_graph_node[]){
+           /* Minimal topology example: callbacks and constraints are omitted. */
+           [TUNNEL_NODE_START] = { .name = "START" },
+           [TUNNEL_NODE_ETH] = {
+               .name = "ETH",
+               .type = RTE_FLOW_ITEM_TYPE_ETH,
+           },
+           [TUNNEL_NODE_OUTER_IPV4] = {
+               .name = "OUTER_IPV4",
+               .type = RTE_FLOW_ITEM_TYPE_IPV4,
+           },
+           [TUNNEL_NODE_UDP] = {
+               .name = "UDP",
+               .type = RTE_FLOW_ITEM_TYPE_UDP,
+           },
+           [TUNNEL_NODE_GTPU] = {
+               .name = "GTPU",
+               .type = RTE_FLOW_ITEM_TYPE_GTPU,
+           },
+           [TUNNEL_NODE_INNER_IPV4] = {
+               .name = "INNER_IPV4",
+               .type = RTE_FLOW_ITEM_TYPE_IPV4,
+           },
+           [TUNNEL_NODE_TCP] = {
+               .name = "TCP",
+               .type = RTE_FLOW_ITEM_TYPE_TCP,
+           },
+           [TUNNEL_NODE_END] = {
+               .name = "END",
+               .type = RTE_FLOW_ITEM_TYPE_END,
+           },
+       },
+       .edges = (struct flow_graph_edge[]){
+           [TUNNEL_NODE_START] = {
+               .next = (size_t[]){
+                   TUNNEL_NODE_ETH,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_ETH] = {
+               .next = (size_t[]){
+                   TUNNEL_NODE_OUTER_IPV4,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_OUTER_IPV4] = {
+               .next = (size_t[]){
+                   TUNNEL_NODE_UDP,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_UDP] = {
+               .next = (size_t[]){
+                   TUNNEL_NODE_GTPU,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_GTPU] = {
+               .next = (size_t[]){
+                   TUNNEL_NODE_INNER_IPV4,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_INNER_IPV4] = {
+               .next = (size_t[]){
+                   TUNNEL_NODE_TCP,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+           [TUNNEL_NODE_TCP] = {
+               .next = (size_t[]){
+                   TUNNEL_NODE_END,
+                   FLOW_GRAPH_NODE_EDGE_END,
+               },
+           },
+       },
+   };
+
+In other words, traversal follows graph edges (node-to-node), while node matching is done against each candidate node's ``rte_flow_item_type``.
+That combination allows repeated protocol layers to be represented cleanly with separate nodes for different parsing contexts.
+
+Although arbitrary loops are possible in the graph, tunnel protocol graphs are usually easier to reason about when repeated item types are split into explicit inner/outer nodes.
+It is not recommended to create loops in the graph, as these loops will be unbounded.
doc/guides/prog_guide/ethdev/index.rst +1 -0
index 392ced0a2e..b41fd045bb 100644
@@ -10,6 +10,7 @@ Ethernet Device Library
     ethdev
     switch_representation
     flow_offload
+    flow_graph
     traffic_metering_and_policing
     traffic_management
     qos_framework
doc/guides/rel_notes/release_26_11.rst +5 -0
index 907f9013ff..663674353d 100644
@@ -64,6 +64,11 @@ New Features
   * Renamed the ``enable_ptype_lldp`` devarg to ``enable_lldp``.
     The old name is no longer accepted.
 
+* **Added internal ethdev flow graph parser helper API.**
+
+  Added ``flow_graph`` helper definitions in ``flow_graph.h``
+  for PMD drivers to build graph-based flow pattern parsers.
+
 
 Removed Items
 -------------
lib/ethdev/flow_graph.h new file +507 -0

507 lines not on this page · raw message

lib/ethdev/meson.build +1 -0
index 8ba6c708a2..99ff3c990c 100644
@@ -40,6 +40,7 @@ driver_sdk_headers += files(
         'ethdev_pci.h',
         'ethdev_vdev.h',
         'rte_flow_driver.h',
+        'flow_graph.h',
         'rte_mtr_driver.h',
         'rte_tm_driver.h',
 )
Signature · 1 line
2.52.0
#33 lore raw

PATCH v2 2/19 net/intel/common: add flow engines infrastructure

In reply to Anatoly Burakov

Current implementation of flow engines in various drivers have a few issues that need to be corrected.

For one, some of the are fundamentally incompatible with secondary processes, because the flow engine registration and creation will allocate structures in shared memory but use process-local pointers to point to flow engines and pattern tables.

For another, a lot of them are needlessly complicated and rely on a separation between patterns and parsing that is hard to reason about and
maintain: they do not define memory ownership model, they do not define the way in which we approach parameter and pattern parsing, and they occasionally do weird things like passing around pointers-to-void-pointers or even using pointers as integer values.

Another common problem is extremely convoluted internal tracking, flow installation, flow replay, and cleanup code. This infrastructure is usually done in an ad-hoc manner that has a lot of boilerplate.

These issues can be corrected, but because of how much code there is to the current infrastructure and how tightly coupled it is, it would be easier to just build new one from scratch, and gradually migrate all engines to use it. This patch is intended as a first step towards that goal, and defines both common data types to be used by all rte_flow parsers, as well as the interaction model that is to be followed by all drivers.

We define a set of structures that will represent:

- Defined rte_flow parsing interaction model and code flow (ops struct)
- Defined memory allocation and ownership model for all engines
- Scratch space format for all engines (variably allocated typed struct)
- Flow rule format for all engines (variably allocated typed struct)
- Engine definitions that are compatible with secondary process model
- Implementations of common rte_flow operations
- Various supporting infrastructure for parser customization, e.g. hooks
- Support for using custom allocation (e.g. for mempool-based alloc)
- Support for replaying all flows to restore HW state
- Support for removing all flows without modifying HW state

The design intent is heavily documented right inside the header and is to be considered authoritative design document for how to build rte_flow parsers for Intel Ethernet drivers going forward.

---
 drivers/net/intel/common/flow_engine.h | 1613 ++++++++++++++++++++++++
 1 file changed, 1613 insertions(+)
 create mode 100644 drivers/net/intel/common/flow_engine.h
drivers/net/intel/common/flow_engine.h new file +1613 -0
new file mode 100644
index 0000000000..3a1d93f55c
@@ -0,0 +1,1613 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef _COMMON_INTEL_FLOW_ENGINE_H_
+#define _COMMON_INTEL_FLOW_ENGINE_H_
+
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/queue.h>
+
+#include <rte_bitops.h>
+#include <rte_malloc.h>
+
+#include <ethdev_driver.h>
+#include <rte_flow.h>
+#include <flow_graph.h>
+#include <rte_tailq.h>
+#include <rte_rwlock.h>
+#include <rte_hexdump.h>
+
+#include "log.h"
+
+/*
+ * This is a common header for Intel Ethernet drivers' flow engine
+ * implementations. It defines the interfaces and data structures required to
+ * implement flow rule engines that can be plugged into the drivers' flow
+ * handling logic.
+ *
+ * Design considerations:
+ *
+ * 1. Ease of implementation
+ *
+ * The flow engine interface is designed to be as simple as possible with
+ * obvious defaults (i.e. not specifying something leads to behavior that
+ * would've been the most expected in context). The point is not to produce a
+ * monstrous driver-within-a-driver framework, but rather to make engine
+ * definitions follow semantic expectations of what the engine actually does.
+ *
+ * All the boilerplate (flow management, engine enablement tracking, etc.) is
+ * handled by the common flow infrastructure, so the engine implementation only
+ * needs to focus on the actual logic of parsing and installing/uninstalling
+ * flow rules, and defining each step of the process as it pertains to each flow
+ * engine.
+ *
+ * It is expected that drivers will use other utility functions from the common
+ * flow-related code where applicable (e.g. flow_util.h, flow_check.h, etc.).
+ *
+ * 2. Full secondary process compatibility
+ *
+ * In order to support rte_flow operations in secondary processes, we need to
+ * store which engines are enabled for particular driver instance, and resolve
+ * them at runtime. The engine index (its position in the engine list) is used as
+ * a bit position in a driver-specific 64-bit field of enabled engines. This
+ * way, the engine definitions can be stored in read-only memory, and referenced
+ * by both primary and secondary processes without issues.
+ *
+ * For this to remain safe, flow engine lists and engine definitions must be
+ * immutable for process lifetime (declare them as const).
+ *
+ * Note that this does not imply that all drivers are therefore able to support
+ * rte_flow-related operations in secondary processes - that is still up to each
+ * driver to implement. This just ensures that the flow engine framework does
+ * not prevent it.
+ *
+ * Engine callbacks must not access or retain an `struct rte_eth_dev *` pointer,
+ * as that object is process-local; use the process-independent
+ * `struct rte_eth_dev_data *` provided by the framework instead.
+ *
+ * The per-instance engine configuration is set up and torn down exclusively by
+ * `ci_flow_engine_conf_init()` and `ci_flow_engine_conf_reset()`. These functions
+ * should only be called at device setup/teardown by primary process.
+ *
+ * 3. Flow object lifecycle is framework-owned
+ *
+ * Engines are expected to treat framework-provided context and flow objects as
+ * storage they fill in, not storage they own. In other words, engine logic
+ * should focus on contents of flow data, while object lifetime is managed by
+ * the framework. Engines may still allocate auxiliary data, but only in places
+ * where the framework guarantees a matching teardown call, which will give the
+ * engine the opportunity to release said auxiliary data.
+ *
+ * 4. Pattern parsing: flow_graph and pattern_parse callback
+ *
+ * The flow engine framework is designed to work hand-in-hand with the
+ * `flow_graph` parsing infrastructure. Each engine may provide a pattern
+ * graph that is used to match the flow pattern, and extract relevant data
+ * into the engine context provided by the framework.
+ *
+ * Engines may also provide a `pattern_parse` callback that is invoked before
+ * the graph parser runs. This allows engines to handle pattern items that
+ * don't fit neatly into the graph model (e.g. FUZZY items that can appear at
+ * any position), as well as ignoring the graph parser entirely and implementing
+ * custom pattern parsing.
+ *
+ * There is no way to completely ignore pattern contents for the engine except
+ * for defining a noop `pattern_parse` callback. This is by design, as such case
+ * is considered rte_flow API misuse. By default, even for empty fallback case,
+ * a meaningful pattern (one that is not empty or ANY) will be treated as error.
+ *
+ * 5. Setup, teardown, and flow list lifecycle ordering
+ *
+ * `ci_flow_engine_conf_init()` and `ci_flow_engine_conf_reset()` are
+ * primary-process-only (see point 2), and the framework does not serialize
+ * them against concurrent flow operations or against each other - the driver
+ * must do so.
+ *
+ * The expected sequence of calls for a driver instance is:
+ *
+ * - `ci_flow_engine_conf_init()` should run from the driver's `dev_init` path.
+ *
+ * - At `dev_close`, `ci_flow_cleanup()` should be run first to drop all flows
+ *   and their internal tracking. Then, `ci_flow_engine_conf_reset()` can be run.
+ *
+ * - Devices may or may not advertise `RTE_ETH_DEV_CAPA_FLOW_RULE_KEEP`,
+ *   i.e. support for keeping flow rules across a `dev_stop`/`dev_start`
+ *   cycle.
+ *
+ * - If the device does not advertise this capability, flows must be
+ *   flushed via `ci_flow_flush()` as the first step of `dev_stop()` (doing so
+ *   later may interfere with flow uninstall).
+ *
+ * - If rule replay is needed (i.e. flows were kept rather than flushed at
+ *   `dev_stop`), the driver should call `ci_flow_replay()` from `dev_start`
+ *   to re-install the kept flows to hardware as last step.
+ */
+
+/* forward declarations for flow engine data types */
+struct ci_flow_engine_ops;
+struct ci_flow_engine_ctx;
+struct ci_flow_engine;
+struct ci_flow_engine_ref;
+struct ci_flow_engine_list;
+struct ci_flow_engine_conf;
+struct ci_flow;
+
+/*
+ * Flow engine ops.
+ *
+ * Each flow engine must provide a set of operations to handle common
+ * operations, such as:
+ *
+ * - Initialize and clean up engine resources (engine_init/engine_uninit)
+ * - Allocate memory for flow rules (flow_alloc)
+ * - Process flow attributes and actions into engine-specific context (ctx_init)
+ * - Parse pattern into engine-specific context (pattern_parse)
+ * - Pattern graph to use when parsing flow patterns (see ethdev's `flow_graph`)
+ * - Any final post-parse checks or actions (ctx_finalize)
+ * - Build the actual flow rule structure from the parsed context (ctx_to_flow)
+ * - Track/untrack the flow rule in driver-internal state (flow_register/flow_unregister)
+ * - Install/remove the flow rule to/from hardware (flow_install/flow_uninstall)
+ * - Query data for the flow rule (flow_query)
+ *
+ * What follows is a description of the flow engine pipeline as it relates to
+ * the flow engine ops struct. Each stage has a setup step (run when a flow is
+ * created) and, where applicable, a teardown step (run when a flow is removed).
+ * Each operation is then a composition of stages: setup steps run in forward
+ * order, teardown steps in reverse. Optional callbacks are shown in [brackets].
+ *
+ * - Allocation stage
+ *     setup:    [flow_alloc]      teardown: [flow_free]
+ *   Provides engine-specific flow object allocation. Skipped on the validate path.
+ *
+ * - Parse stage
+ *     setup:    ctx_init -> [pattern_parse] -> [graph parser] ->
+ *               [ctx_finalize] -> [ctx_to_flow]
+ *     teardown: (none)
+ *   Interprets the request into the flow structure. Must NOT allocate.
+ *
+ * - Track stage
+ *     setup:    [flow_register]   teardown: [flow_unregister]
+ *   Updates driver-internal state. Permitted to allocate.
+ *
+ * - Apply stage
+ *     setup:    [flow_install]    teardown: [flow_uninstall]
+ *   Programs the hardware. Must NOT allocate.
+ *
+ * Typical operation sequences:
+ *
+ * - rte_flow_create:   Allocation -> Parse -> Track -> Apply   (setup)
+ * - rte_flow_validate: Parse
+ * - rte_flow_destroy:  Apply -> Track -> Allocation            (teardown)
+ * - rte_flow_flush:    Apply -> Track -> Allocation            (teardown, per flow)
+ * - ci_flow_replay:    Apply                                   (setup, per flow)
+ * - ci_flow_cleanup:   Track -> Allocation                     (teardown, per flow)
+ *
+ * 1) Engine availability and lifecycle
+ *
+ * The engine availability can be checked by the driver at init time. The exact
+ * mechanics of this is left up to each individual driver - it may be hardware
+ * capability bits, PHY type check, devargs, or any other criteria that makes
+ * sense in the context of driver/adapter. If the engine_init callback is not
+ * implemented, the engine is assumed to be always available.
+ *
+ * If `priv_size` is non-zero, the framework allocates a zeroed private block
+ * and passes it to engine_init/engine_uninit. Each allocated flow will also
+ * carry a pointer to this per-device private data in `flow->engine_priv`.
+ *
+ * 2) Input parsing
+ *
+ * `ctx_init` is mandatory and acts as the main gateway for processing actions and
+ * attributes into engine context.
+ *
+ * Patterns are matched either against the pattern graph provided by the
+ * `ci_flow_engine` structure, or by the `pattern_parse` callback, or both.
+ *
+ * Pattern handling mode is selected from graph/callback presence:
+ *
+ * - graph only: graph parser runs
+ * - callback only: pattern_parse runs
+ * - callback + graph: pattern_parse runs, then graph parser runs
+ * - neither graph nor callback: empty fallback matcher is used
+ *
+ * Empty fallback matcher accepts:
+ *
+ * - NULL pattern
+ * - empty pattern (start -> end)
+ * - ANY-only pattern (start -> any -> end)
+ *
+ * Any other patterns will be rejected by the empty path.
+ *
+ * In callback + graph mode, the callback does not remove items from the
+ * original pattern stream. The graph parser still sees the same pattern, so the
+ * graph should define its `ignore_nodes` list appropriately.
+ *
+ * 3) Rule finalization
+ *
+ * `ctx_finalize` is an optional final check for consistency between parsed
+ * action/attribute data and pattern-derived data, as well as a chance to perform
+ * any final post-parse operations.
+ *
+ * 4) Rule materialization
+ *
+ * `ctx_to_flow` translates parsed context into the concrete engine-specific
+ * flow rule representation. As a general guideline, flow rule should not
+ * contain anything that isn't useful for rule installation or querying. All
+ * of the temporary data should be stored in the flow building context, while
+ * the rule should only contain data that is pertinent to flow programming.
+ *
+ * 5) Rule lifecycle hooks:
+ *
+ * `flow_alloc`/`flow_free` are optional custom object lifecycle callbacks
+ * (Allocation stage). If `flow_alloc` is not provided (or returns NULL), the
+ * framework falls back to rte_zmalloc-based allocation. If `flow_alloc` callback
+ * implementation exists, `flow_free` implementation must exist as well.
+ *
+ * `flow_register`/`flow_unregister` are the optional Track stage callbacks.
+ * `flow_register` builds driver-internal state for the flow; `flow_unregister`
+ * tears it down. As with alloc/free, they must both be defined or both be NULL.
+ *
+ * `flow_install`/`flow_uninstall` are the optional Apply stage callbacks that
+ * program and unprogram the hardware. They must not allocate.
+ *
+ * The engines own only engine-specific fields. The common `ci_flow` fields
+ * (engine_idx, engine_priv, fallback_alloc, etc.) are owned and managed by the
+ * framework and are not to be modified.
+ *
+ * IMPORTANT: by the time the Track and Apply stages run, the engine has already
+ * accepted the flow through `ctx_init`/`ctx_finalize`/`ctx_to_flow`. A failure
+ * in `flow_register` or `flow_install` (e.g. hardware/software resource
+ * exhaustion) is treated as a hard failure: no other engines will be tried. It
+ * is therefore implied that if the flow parser has accepted the flow, it should
+ * in principle be trackable and installable - any checks intrinsic to the pattern
+ * itself that would have prevented that should have been done at the parse stage.
+ *
+ * 6) Memory ownership and allocation policy:
+ *
+ * There are exactly three places where an engine is permitted to do allocations:
+ *
+ * - engine init
+ * - flow alloc (Allocation stage)
+ * - flow register (Track stage)
+ *
+ * In engine initialization, the engine may allocate any engine-wide resources
+ * that will later be utilized by other callbacks (such as creating a memory
+ * pool from which `flow_alloc` will allocate flow objects).
+ *
+ * The flow allocation callback is an allocation-policy callback only. That is,
+ * a well-behaved implementation for this callback may allocate memory *for the
+ * flow itself* (such as getting an object from a mempool), and it may update
+ * allocator-associated state (such as tracking allocated objects), but it
+ * otherwise must not produce any artifacts to be used by the parsing stage.
+ * This is because for `rte_flow_validate()`, the `flow_alloc` callbacks are
+ * bypassed, so parse stage cannot rely on anything `flow_alloc` may have done.
+ *
+ * The flow register callback is where the transition from "parsing the flow" to
+ * "updating driver-internal state" happens, and is the only per-flow callback
+ * where a well-behaved implementation may allocate memory and store a pointer
+ * in the flow object or in any internal driver structure.
+ *
+ * The flow install callback must NOT allocate: it only programs the hardware.
+ *
+ * Parsing-related callbacks (`ctx_init`, `pattern_parse`, `ctx_finalize`,
+ * `ctx_to_flow`) must not allocate any memory, as the parsing control flow may
+ * fail at any of these steps without there being any teardown.
+ *
+ * 7) Flow query
+ *
+ * For querying data, the flow_query function is provided to query data for the
+ * flow rule. The engine may not provide this function, in which case any
+ * attempt to query the rule will result in failure.
+ *
+ * 8) Concurrency model
+ *
+ * The framework serializes access to each driver instance's flow state with a
+ * single rwlock. Create, destroy and flush take it exclusively (write);
+ * validate and query take it shared (read). Consequently the parse-phase
+ * callbacks (ctx_init, pattern_parse, ctx_finalize, ctx_to_flow) can run
+ * concurrently with each other on the validate path, and flow_query can run
+ * concurrently with other queries. These callbacks must treat shared engine
+ * state - including engine_priv - as read-only and must not mutate it. State
+ * mutation belongs in the write-locked callbacks (engine_init/engine_uninit,
+ * flow_alloc/flow_free, flow_register/flow_unregister,
+ * flow_install/flow_uninstall).
+ */
+struct ci_flow_engine_ops {
+	/* engine init callback - can be NULL */
+	int (*engine_init)(const struct ci_flow_engine *engine,
+			struct rte_eth_dev_data *dev_data,
+			void *priv);
+	/* engine uninit callback - can be NULL */
+	void (*engine_uninit)(const struct ci_flow_engine *engine,
+			void *priv);
+	/* allocation callback for flow rules - can be NULL */
+	struct ci_flow *(*flow_alloc)(const struct ci_flow_engine *engine,
+			struct rte_eth_dev_data *dev_data,
+			void *priv);
+	/* deallocation callback for flow rules - can be NULL */
+	void (*flow_free)(struct ci_flow *flow,
+			struct rte_eth_dev_data *dev_data,
+			void *priv);
+	/* initialize engine context from flow attr/actions - mandatory */
+	int (*ctx_init)(const struct rte_flow_action actions[],
+			const struct rte_flow_attr *attr,
+			struct ci_flow_engine_ctx *ctx,
+			struct rte_flow_error *error);
+	/* pattern parsing callback - can be NULL */
+	int (*pattern_parse)(struct ci_flow_engine_ctx *ctx,
+			const struct rte_flow_item pattern[],
+			struct rte_flow_error *error);
+	/* final pass before converting context to flow - can be NULL */
+	int (*ctx_finalize)(struct ci_flow_engine_ctx *ctx,
+			struct rte_flow_error *error);
+	/* initialize flow rule from parsed context - can be NULL */
+	int (*ctx_to_flow)(const struct ci_flow_engine_ctx *ctx,
+			struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* track a flow rule in driver-internal state - can be NULL */
+	int (*flow_register)(struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* untrack a flow rule from driver-internal state - can be NULL */
+	int (*flow_unregister)(struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* install a flow rule to hardware - can be NULL */
+	int (*flow_install)(struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* uninstall a flow rule from hardware - can be NULL */
+	int (*flow_uninstall)(struct ci_flow *flow,
+			struct rte_flow_error *error);
+	/* query flow - can be NULL */
+	int (*flow_query)(struct ci_flow *flow,
+			const struct rte_flow_action *action,
+			void *data,
+			struct rte_flow_error *error);
+};
+
+/*
+ * common definition for flow engine context.
+ * each engine will define its own context structure that
+ * *must* start with this base structure.
+ */
+struct ci_flow_engine_ctx {
+	/* ethernet device this context belongs to */
+	struct rte_eth_dev_data *dev_data;
+	/* original flow attributes, as passed by the caller */
+	const struct rte_flow_attr *attr;
+	/* original flow pattern, as passed by the caller */
+	const struct rte_flow_item *pattern;
+	/* original flow actions, as passed by the caller */
+	const struct rte_flow_action *actions;
+};
+
+/*
+ * Common definition for flow rule.
+ *
+ * For flow rules, there are three parts to consider:
+ *
+ * 1) Common data
+ * 2) Driver-specific data
+ * 3) Engine-specific data
+ *
+ * The common data is defined here as the `ci_flow` structure. It contains
+ * fields that are common to all flow rules, regardless of driver or engine.
+ * This includes a linked list node for managing flow rules in a list, a pointer
+ * to the device (driver instance) the flow belongs to, and the engine index
+ * that created the flow.
+ *
+ * With rte_flow API, each driver is meant to define its own rte_flow structure
+ * that contains driver-specific data. This structure must start with the
+ * `ci_flow` structure defined here, followed by driver-specific fields.
+ *
+ * Additionally, each *engine* may want to define its own flow rule structure
+ * that contains actual engine-specific data. This structure must start with the
+ * driver-wide `rte_flow` structure such that it contains everything before it,
+ * followed by engine-specific fields.
+ *
+ * IMPORTANT:
+ *
+ * All of these structures will be referred to by the same pointer and can be
+ * freely (and safely) cast between each other *as long as* each structure
+ * definition has the parent structure as its first member. E.g. the common flow
+ * struct is `ci_flow`, and the driver-specific `rte_flow` must be defined as
+ * follows:
+ *
+ * struct rte_flow {
+ *     struct ci_flow base;
+ *     ...any driver-specific fields...
+ * }
+ *
+ * If the engine needs to define its own flow structure, in turn it should be
+ * defined as follows:
+ *
+ * struct ixgbe_fdir_flow {
+ *     struct rte_flow base;
+ *     ...any engine-specific fields...
+ * }
+ *
+ * This ensures pointer conversion safety between all three types:
+ *
+ *     struct ci_flow *flow = ...;
+ *     struct rte_flow *rte_flow = (struct rte_flow *)flow;
+ *     struct engine_specific_flow *es_flow = (struct engine_specific_flow *)flow;
+ *
+ * The engine structure provides a `flow_size` field that indicates how much
+ * memory is required for a particular engine's flow structure. The driver must
+ * provide that value for each engine, as it will be used to size flow structure
+ * allocations. If the engine does not require any memory beyond the `rte_flow`
+ * structure, this value should be set to `sizeof(rte_flow)` for those engines,
+ * thus making it equal to sizeof(ci_flow) plus any driver-specific data.
+ *
+ * Engine references and engine_idx:
+ *
+ * The engine index is treated as the engine type discriminator and is stored in
+ * every flow. Runtime code then reconstructs a `ci_flow_engine_ref`
+ * (engine pointer + index) from the immutable engine list and this stored
+ * index. This gives two important guarantees:
+ *
+ * - The framework works only with immutable engine definitions/lists.
+ * - We avoid repeated pointer-to-index lookups after flow creation; rebuilding
+ *   a ref from index is direct and keeps call sites explicit about both values.
+ *
+ * This is particularly important for secondary-process compatibility where the
+ * index is the stable identity and pointer values are only meaningful in the
+ * context of the shared immutable engine list.
+ *
+ * Code that works with engine pointers and engine indices should be careful,
+ * and should treat engine references as the basic building block.
+ *
+ * Per-engine private data (engine_priv):
+ *
+ * Each flow will also carry a pointer to engine-private data, to allow for
+ * reaching engine-specific data, such as custom allocation structures to
+ * be used with flow_alloc/flow_free at allocation time.
+ *
+ * Fallback allocation flag:
+ *
+ * When `flow_alloc` is provided by the driver, but it returns NULL, the framework
+ * will fall back to rte_zmalloc()-based allocation. The `fallback_alloc` flag is
+ * then used to route subsequent deallocation to rte_free(). This implies that for
+ * an engine, `flow_alloc` returning NULL *is not considered to indicate an error*.
+ */
+struct ci_flow {
+	TAILQ_ENTRY(ci_flow) node;
+	/* device this flow belongs to */
+	struct rte_eth_dev_data *dev_data;
+	/* index of engine this flow was created by */
+	size_t engine_idx;
+	/* per-engine private data pointer, set by the framework at alloc time */
+	void *engine_priv;
+	/* set if engine allocator callback existed but fallback allocator was used */
+	bool fallback_alloc;
+};
+
+/* flow engine definition */
+struct ci_flow_engine {
+	/* engine name */
+	const char *name;
+	/* size of scratch space structure, can be 0 */
+	size_t ctx_size;
+	/* size of flow rule structure, must not be 0 */
+	size_t flow_size;
+	/* size of per-device engine private data, can be 0 */
+	size_t priv_size;
+	/* ops for this flow engine */
+	const struct ci_flow_engine_ops *ops;
+	/* pattern graph this engine supports - can be NULL */
+	const struct flow_graph *graph;
+};
+
+/* engine reference: immutable engine pointer paired with its list index */
+struct ci_flow_engine_ref {
+	const struct ci_flow_engine *engine;
+	size_t engine_idx;
+};
+
+#define CI_FLOW_ENGINE_MAX	64
+
+/* flow engine list definition */
+struct ci_flow_engine_list {
+	/* NULL-terminated immutable array of flow engine pointers */
+	const struct ci_flow_engine * const engines[CI_FLOW_ENGINE_MAX];
+};
+
+/* flow engine configuration - each device must have its own instance */
+struct ci_flow_engine_conf {
+	/* lock to protect config */
+	rte_rwlock_t config_lock;
+	/* list of flows created on this device */
+	TAILQ_HEAD(ci_flow_list, ci_flow) flows;
+	/* bitmask of enabled engines */
+	uint64_t enabled_engines;
+	/* back-reference to device structure */
+	struct rte_eth_dev_data *dev_data;
+	/* reference to the driver's engine list */
+	const struct ci_flow_engine_list *engines;
+	/* per-engine private data pointers, indexed by engine index */
+	void *engine_priv[CI_FLOW_ENGINE_MAX];
+};
+
+/* build engine ref from engine config and engine index - caller must hold config lock */
+static inline struct ci_flow_engine_ref
+ci_flow_engine_ref_from_idx(const struct ci_flow_engine_conf *engine_conf,
+		const size_t engine_idx)
+{
+	/* check if valid reference can be constructed at all */
+	if (engine_conf == NULL || engine_conf->engines == NULL ||
+			engine_idx >= CI_FLOW_ENGINE_MAX) {
+		return (struct ci_flow_engine_ref) {
+			.engine = NULL,
+			.engine_idx = CI_FLOW_ENGINE_MAX
+		};
+	}
+	/* the index itself is valid, but there may be no engine */
+	if (engine_conf->engines->engines[engine_idx] == NULL) {
+		return (struct ci_flow_engine_ref) {
+			.engine = NULL,
+			.engine_idx = CI_FLOW_ENGINE_MAX
+		};
+	}
+
+	return (struct ci_flow_engine_ref) {
+		.engine = engine_conf->engines->engines[engine_idx],
+		.engine_idx = engine_idx,
+	};
+}
+
+/* helper macro to iterate over list of engines as engine refs */
+#define CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf)                         \
+	for (size_t __ci_flow_engine_idx = 0;                                        \
+		__ci_flow_engine_idx < CI_FLOW_ENGINE_MAX &&                         \
+			(((engine_ref) = ci_flow_engine_ref_from_idx((engine_conf),  \
+		__ci_flow_engine_idx)).engine != NULL);                              \
+		__ci_flow_engine_idx++)
+
+/* basic checks for flow engine validity */
+static inline bool
+ci_flow_engine_is_valid(const struct ci_flow_engine *engine)
+{
+	/* is the pointer valid? */
+	if (engine == NULL)
+		return false;
+	/* does the engine have a name? */
+	if (engine->name == NULL)
+		return false;
+	/* does the engine have ops? */
+	if (engine->ops == NULL)
+		return false;
+	/* does the engine have mandatory ctx_init op? */
+	if (engine->ops->ctx_init == NULL)
+		return false;
+	/* flow size cannot be less than ci_flow */
+	if (engine->flow_size < sizeof(struct ci_flow))
+		return false;
+	/* alloc and free must both be defined or NULL */
+	if ((engine->ops->flow_alloc == NULL) != (engine->ops->flow_free == NULL))
+		return false;
+	/* register and unregister must both be defined or NULL */
+	if ((engine->ops->flow_register == NULL) != (engine->ops->flow_unregister == NULL))
+		return false;
+	/* engine looks valid */
+	return true;
+}
+
+/* helper to check whether an engine is enabled in the bitmask - caller must hold config lock */
+static inline bool
+ci_flow_engine_is_enabled(const struct ci_flow_engine_conf *conf,
+		const size_t engine_idx)
+{
+	return (conf->enabled_engines & RTE_BIT64(engine_idx)) != 0;
+}
+
+/* helper to enable an engine in the bitmask - caller must hold config lock */
+static inline void
+ci_flow_engine_set_enabled(struct ci_flow_engine_conf *conf, const size_t engine_idx,
+		bool enabled)
+{
+	if (enabled)
+		conf->enabled_engines |= RTE_BIT64(engine_idx);
+	else
+		conf->enabled_engines &= ~RTE_BIT64(engine_idx);
+}
+
+static inline struct ci_flow *
+ci_flow_alloc(const struct ci_flow_engine_conf *engine_conf,
+		struct ci_flow_engine_ref engine_ref)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+	void *priv = engine_conf->engine_priv[engine_ref.engine_idx];
+	struct ci_flow *flow = NULL;
+	bool fallback = false;
+
+	/* if engine has an allocator callback, try it first */
+	if (engine->ops->flow_alloc != NULL)
+		flow = engine->ops->flow_alloc(engine, engine_conf->dev_data, priv);
+	/* if allocator callback is not defined or failed, use default allocator */
+	if (flow == NULL) {
+		flow = (struct ci_flow *)rte_zmalloc(NULL, engine->flow_size, 0);
+
+		/* if callback exists and we're here, allocation has fallen back */
+		if (flow != NULL && engine->ops->flow_alloc != NULL)
+			fallback = true;
+	}
+	/* set the engine data to enable correct deallocation in case of failure */
+	if (flow != NULL) {
+		/* erase the common parts - the rest is left up to the engine */
+		memset(flow, 0, sizeof(struct ci_flow));
+		flow->fallback_alloc = fallback;
+		flow->engine_idx = engine_ref.engine_idx;
+		flow->dev_data = engine_conf->dev_data;
+		flow->engine_priv = priv;
+	}
+	return flow;
+}
+
+static inline void
+ci_flow_free(struct ci_flow_engine_ref engine_ref, struct ci_flow *flow)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	if (engine->ops->flow_free != NULL && !flow->fallback_alloc)
+		engine->ops->flow_free(flow, flow->dev_data, flow->engine_priv);
+	else
+		rte_free(flow);
+}
+
+/* track a flow in driver-internal state (Track stage) - caller must hold config lock */
+static inline int
+ci_flow_register(struct ci_flow_engine_ref engine_ref,
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	if (engine->ops->flow_register != NULL)
+		return engine->ops->flow_register(flow, error);
+
+	return 0;
+}
+
+/* untrack a flow from driver-internal state (Track stage) - caller must hold config lock */
+static inline int
+ci_flow_unregister(struct ci_flow_engine_ref engine_ref,
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	if (engine->ops->flow_unregister != NULL)
+		return engine->ops->flow_unregister(flow, error);
+
+	return 0;
+}
+
+/* allocate per-device engine private data and call init - caller must hold config lock */
+static inline int
+ci_flow_engine_init(struct ci_flow_engine_conf *engine_conf,
+		struct ci_flow_engine_ref engine_ref)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+	void *priv = NULL;
+	int ret;
+
+	if (engine->priv_size > 0) {
+		priv = rte_zmalloc(engine->name, engine->priv_size, 0);
+		if (priv == NULL) {
+			ret = -ENOMEM;
+			goto err;
+		}
+	}
+
+	if (engine->ops->engine_init != NULL) {
+		ret = engine->ops->engine_init(engine, engine_conf->dev_data, priv);
+		if (ret != 0)
+			goto err;
+	}
+	engine_conf->engine_priv[engine_ref.engine_idx] = priv;
+	return 0;
+err:
+	rte_free(priv);
+	return ret;
+}
+
+/* call uninit and free per-device engine private data - caller must hold config lock */
+static inline void
+ci_flow_engine_uninit(struct ci_flow_engine_conf *engine_conf,
+		struct ci_flow_engine_ref engine_ref)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+	void *priv;
+
+	priv = engine_conf->engine_priv[engine_ref.engine_idx];
+
+	CI_DRV_LOG(DEBUG, "engine '%s': uninit", engine->name);
+	/* ignore uninit errors */
+	if (engine->ops->engine_uninit != NULL)
+		engine->ops->engine_uninit(engine, priv);
+
+	rte_free(priv);
+	engine_conf->engine_priv[engine_ref.engine_idx] = NULL;
+}
+
+/* disable all engines for a specific driver instance - caller must serialize teardown */
+static inline void
+ci_flow_engine_conf_reset(struct ci_flow_engine_conf *engine_conf)
+{
+	struct ci_flow_engine_ref engine_ref;
+	struct ci_flow *flow, *tmp;
+
+	/* free all flows - shouldn't have any at this point */
+	RTE_TAILQ_FOREACH_SAFE(flow, &engine_conf->flows, node, tmp) {
+		struct rte_flow_error unreg_error = { 0 };
+		int unreg_ret;
+
+		TAILQ_REMOVE(&engine_conf->flows, flow, node);
+		/* can't make it into the list if this was invalid, so no checks */
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf, flow->engine_idx);
+		/* untrack before free; ignore errors on this teardown path */
+		unreg_ret = ci_flow_unregister(engine_ref, flow, &unreg_error);
+		if (unreg_ret != 0)
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to unregister flow: %s",
+					engine_ref.engine->name, unreg_error.message);
+		ci_flow_free(engine_ref, flow);
+	}
+
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		if (!ci_flow_engine_is_enabled(engine_conf, engine_ref.engine_idx))
+			continue;
+		/* ignore errors */
+		ci_flow_engine_uninit(engine_conf, engine_ref);
+		ci_flow_engine_set_enabled(engine_conf,
+				engine_ref.engine_idx, false);
+	}
+
+	/* erase device pointer */
+	engine_conf->dev_data = NULL;
+	engine_conf->engines = NULL;
+}
+
+/* enable all engines for a specific driver instance - caller must serialize initialization */
+static inline int
+ci_flow_engine_conf_init(struct ci_flow_engine_conf *engine_conf,
+		const struct ci_flow_engine_list *engine_list,
+		struct rte_eth_dev_data *dev_data)
+{
+	struct ci_flow_engine_ref engine_ref;
+
+	/* reject invalid configuration */
+	if (engine_conf == NULL || engine_list == NULL || dev_data == NULL)
+		return -1;
+
+	/* init the lock */
+	rte_rwlock_init(&engine_conf->config_lock);
+
+	/* store data in conf */
+	engine_conf->dev_data = dev_data;
+	engine_conf->engines = engine_list;
+
+	/* init the flow list */
+	TAILQ_INIT(&engine_conf->flows);
+
+	/* enable all engines */
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		/* skip invalid engines */
+		if (!ci_flow_engine_is_valid(engine_ref.engine)) {
+			CI_DRV_LOG(DEBUG, "engine[%zu]: invalid, skipping",
+					engine_ref.engine_idx);
+			continue;
+		}
+		if (ci_flow_engine_init(engine_conf, engine_ref) != 0) {
+			CI_DRV_LOG(DEBUG, "engine '%s': init failed, skipping",
+					engine_ref.engine->name);
+			continue;
+		}
+
+		ci_flow_engine_set_enabled(engine_conf,
+				engine_ref.engine_idx, true);
+		CI_DRV_LOG(DEBUG, "engine '%s': enabled", engine_ref.engine->name);
+	}
+	return 0;
+}
+
+/* check whether a flow is valid for a specific engine configuration - caller must hold config lock */
+static inline bool
+ci_flow_is_valid(const struct ci_flow *flow,
+		const struct ci_flow_engine_conf *engine_conf)
+{
+	/* is the pointer valid? */
+	if (flow == NULL)
+		return false;
+	/* is the conf initialized? */
+	if (engine_conf == NULL || engine_conf->dev_data == NULL ||
+			engine_conf->engines == NULL)
+		return false;
+	/* does the flow belong to this device? */
+	if (flow->dev_data != engine_conf->dev_data)
+		return false;
+	/* can we find the engine that created this flow? */
+	if (flow->engine_idx >= CI_FLOW_ENGINE_MAX)
+		return false;
+	/* engine must be enabled */
+	if (!ci_flow_engine_is_enabled(engine_conf, flow->engine_idx))
+		return false;
+	/* flow looks valid */
+	return true;
+}
+
+/* default empty pattern graph definitions */
+enum ci_flow_empty_graph_node_id {
+	CI_FLOW_EMPTY_GRAPH_NODE_START = FLOW_GRAPH_NODE_FIRST,
+	CI_FLOW_EMPTY_GRAPH_NODE_ANY,
+	CI_FLOW_EMPTY_GRAPH_NODE_END,
+};
+
+static const struct flow_graph ci_flow_empty_graph = {
+	.nodes = (struct flow_graph_node []) {
+		[CI_FLOW_EMPTY_GRAPH_NODE_START] = {
+			.name = "START",
+		},
+		[CI_FLOW_EMPTY_GRAPH_NODE_ANY] = {
+			.name = "ANY",
+			.type = RTE_FLOW_ITEM_TYPE_ANY,
+			.constraints = FLOW_GRAPH_NODE_EXPECT_EMPTY,
+		},
+		[CI_FLOW_EMPTY_GRAPH_NODE_END] = {
+			.name = "END",
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+	},
+	.edges = (struct flow_graph_edge []) {
+		[CI_FLOW_EMPTY_GRAPH_NODE_START] = {
+			.next = (size_t []) {
+				CI_FLOW_EMPTY_GRAPH_NODE_ANY,
+				CI_FLOW_EMPTY_GRAPH_NODE_END,
+				FLOW_GRAPH_NODE_EDGE_END,
+			},
+		},
+		[CI_FLOW_EMPTY_GRAPH_NODE_ANY] = {
+			.next = (size_t []) {
+				CI_FLOW_EMPTY_GRAPH_NODE_END,
+				FLOW_GRAPH_NODE_EDGE_END,
+			},
+		},
+	}
+};
+
+/* concrete pattern-matching mode selected from graph/callback presence */
+enum ci_match_type {
+	CI_MATCH_EMPTY,    /* no graph, no callback */
+	CI_MATCH_CALLBACK, /* callback only */
+	CI_MATCH_GRAPH,    /* graph only */
+	CI_MATCH_ALL,      /* callback + graph */
+};
+
+/*
+ * helper to match a pattern against an engine's pattern graph and/or callback,
+ * depending on the match type. Caller must hold config lock.
+ */
+static inline int
+ci_flow_match(const struct ci_flow_engine *engine,
+		const struct rte_flow_item pattern[],
+		struct ci_flow_engine_ctx *ctx,
+		enum ci_match_type match_type,
+		struct rte_flow_error *error)
+{
+	switch (match_type) {
+	case CI_MATCH_EMPTY:
+		/* for empty matching, NULL pattern is not an error */
+		if (pattern != NULL) {
+			return flow_graph_parse(&ci_flow_empty_graph,
+					pattern, error, ctx);
+		}
+		return 0;
+
+	case CI_MATCH_CALLBACK:
+		/* for callback matching, pattern cannot be NULL */
+		if (pattern == NULL) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+					"Pattern cannot be NULL");
+		}
+		return engine->ops->pattern_parse(ctx, pattern, error);
+
+	case CI_MATCH_GRAPH:
+		/* for graph matching, pattern cannot be NULL */
+		if (pattern == NULL) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+					"Pattern cannot be NULL");
+		}
+		return flow_graph_parse(engine->graph, pattern, error, ctx);
+
+	case CI_MATCH_ALL:
+	{
+		int ret;
+
+		/* for callback + graph matching, pattern cannot be NULL */
+		if (pattern == NULL) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+					"Pattern cannot be NULL");
+		}
+		ret = engine->ops->pattern_parse(ctx, pattern, error);
+		if (ret != 0)
+			return ret;
+		return flow_graph_parse(engine->graph, pattern, error, ctx);
+	}
+
+	default:
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, NULL,
+				"Invalid match type");
+	}
+}
+
+/* parse a flow using a specific engine - caller must hold config lock */
+static inline int
+ci_flow_parse(const struct ci_flow_engine_conf *engine_conf,
+		const struct ci_flow_engine *engine,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	enum ci_match_type match_type;
+	struct ci_flow_engine_ctx *ctx;
+	int ret = 0;
+
+	/*
+	 * Determine the type of matching we are going to perform based on the
+	 * presence of pattern graph and pattern_parse callback. The logic is as
+	 * follows:
+	 *
+	 * - if graph but no callback, match against graph
+	 *
+	 * Expected default case: pattern matching is graph based, no special
+	 * handling for any pattern items.
+	 *
+	 * - if both graph and callback, match against callback + graph
+	 *
+	 * Preprocessor case, i.e. preprocess the pattern with the callback
+	 * before handling the matching to the graph engine. The assumption is
+	 * that the graph will be set up with a proper ignore list to skip over
+	 * nodes that weren't meant for the graph processing.
+	 *
+	 * - if no graph but callback, match against callback
+	 *
+	 * Fully custom pattern parsing case.
+	 *
+	 * - if no graph and no callback, match against empty graph
+	 *
+	 * "Pattern is not meaningful" case, for engines that do not care about
+	 * the pattern at all. A default matching behavior against empty
+	 * patterns is provided (i.e. allow NULL pattern, and allow END or ANY
+	 * -> END patterns). Note that this is not the same as ignoring pattern
+	 * entirely: the engine will still reject patterns that are not empty.
+	 */
+	match_type = engine->graph == NULL ?
+		(engine->ops->pattern_parse == NULL ? CI_MATCH_EMPTY : CI_MATCH_CALLBACK) :
+		(engine->ops->pattern_parse == NULL ? CI_MATCH_GRAPH : CI_MATCH_ALL);
+
+	CI_DRV_LOG(DEBUG, "engine '%s': parsing flow", engine->name);
+
+	/* allocate context */
+	ctx = (struct ci_flow_engine_ctx *)calloc(1,
+			RTE_MAX(engine->ctx_size, sizeof(struct ci_flow_engine_ctx)));
+	if (ctx == NULL) {
+		return rte_flow_error_set(error, ENOMEM,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Failed to allocate memory for rule engine context");
+	}
+	ctx->dev_data = engine_conf->dev_data;
+	ctx->attr = attr;
+	ctx->pattern = pattern;
+	ctx->actions = actions;
+	flow->dev_data = engine_conf->dev_data;
+
+	/* parse flow parameters */
+	ret = engine->ops->ctx_init(actions, attr, ctx, error);
+
+	/* context init failed - that means engine can't be used for this flow */
+	if (ret != 0)
+		goto free_ctx;
+
+	/* match the pattern */
+	ret = ci_flow_match(engine, pattern, ctx, match_type, error);
+
+	/* check if pattern didn't match */
+	if (ret != 0)
+		goto free_ctx;
+
+	/* final verification, if the operation is defined */
+	if (engine->ops->ctx_finalize != NULL)
+		ret = engine->ops->ctx_finalize(ctx, error);
+
+	/* finalization failed - mismatch between parsed data and context data */
+	if (ret != 0)
+		goto free_ctx;
+
+	/* if we need to build rules from context, do it */
+	if (engine->ops->ctx_to_flow != NULL) {
+		ret = engine->ops->ctx_to_flow(ctx, flow, error);
+
+		/* flow building failed - something wrong with context data */
+		if (ret != 0)
+			goto free_ctx;
+	}
+	/* success */
+	ret = 0;
+
+free_ctx:
+	free(ctx);
+	return ret;
+}
+
+/* install a flow using its appropriate engine - caller must hold config lock */
+static inline int
+ci_flow_install(struct ci_flow_engine_ref engine_ref,
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	if (engine->ops->flow_install != NULL)
+		return engine->ops->flow_install(flow, error);
+
+	return 0;
+}
+
+/* uninstall a flow using its appropriate engine - caller must hold config lock */
+static inline int
+ci_flow_uninstall(struct ci_flow_engine_ref engine_ref,
+		struct ci_flow *flow,
+		struct rte_flow_error *error)
+{
+	const struct ci_flow_engine *engine = engine_ref.engine;
+
+	/* uninstall the flow if required */
+	if (engine->ops->flow_uninstall != NULL)
+		return engine->ops->flow_uninstall(flow, error);
+
+	return 0;
+}
+
+/*
+ * The following functions are designed to be called from the context of
+ * rte_flow API implementations and are provided as default implementations.
+ *
+ * These default implementations take the config lock internally.
+ */
+
+/* default implementation of rte_flow_create using flow engines */
+static inline struct rte_flow *
+ci_flow_create(struct ci_flow_engine_conf *engine_conf,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error)
+{
+	struct ci_flow_engine_ref engine_ref;
+	struct ci_flow *flow = NULL;
+	int ret;
+
+	if (attr == NULL || actions == NULL) {
+		CI_DRV_LOG(DEBUG, "attr or actions is NULL");
+		rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR, NULL,
+				"Attributes and actions cannot be NULL");
+		return NULL;
+	}
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	/* find an engine that can handle this flow */
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		if (!ci_flow_engine_is_enabled(engine_conf, engine_ref.engine_idx))
+			continue;
+
+		flow = ci_flow_alloc(engine_conf, engine_ref);
+		if (flow == NULL) {
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to allocate flow",
+					engine_ref.engine->name);
+			rte_flow_error_set(error, ENOMEM,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to allocate memory for flow rule");
+			/* this is a serious error so don't continue */
+			goto unlock;
+		}
+
+		ret = ci_flow_parse(engine_conf, engine_ref.engine, attr, pattern,
+				actions, flow, error);
+
+		/* parsing failed - free the flow and try next engine */
+		if (ret != 0) {
+			ci_flow_free(engine_ref, flow);
+			if (error != NULL)
+				CI_DRV_LOG(DEBUG, "engine '%s' rejected flow: %s",
+						engine_ref.engine->name, error->message);
+			else
+				CI_DRV_LOG(DEBUG, "engine '%s' rejected flow",
+						engine_ref.engine->name);
+			continue;
+		}
+
+		CI_DRV_LOG(DEBUG, "engine '%s' accepted flow, committing",
+				engine_ref.engine->name);
+
+		/* engine accepted the flow - Track then Apply are a hard commitment */
+		ret = ci_flow_register(engine_ref, flow, error);
+		if (ret != 0) {
+			/* track failed after parse accepted - do not try other engines */
+			if (error != NULL)
+				CI_DRV_LOG(DEBUG, "engine '%s' register failed: %s",
+						engine_ref.engine->name, error->message);
+			else
+				CI_DRV_LOG(DEBUG, "engine '%s' register failed",
+						engine_ref.engine->name);
+			ci_flow_free(engine_ref, flow);
+			flow = NULL;
+			goto unlock;
+		}
+
+		ret = ci_flow_install(engine_ref, flow, error);
+		if (ret != 0) {
+			struct rte_flow_error unreg_error = { 0 };
+			int unreg_ret;
+
+			/* install failed after parse accepted - this is an
+			 * engine bug, do not try other engines
+			 */
+			if (error != NULL)
+				CI_DRV_LOG(DEBUG, "engine '%s' install failed: %s",
+						engine_ref.engine->name, error->message);
+			else
+				CI_DRV_LOG(DEBUG, "engine '%s' install failed",
+						engine_ref.engine->name);
+			/* roll back the Track stage, preserving the install error */
+			unreg_ret = ci_flow_unregister(engine_ref, flow, &unreg_error);
+			if (unreg_ret != 0)
+				CI_DRV_LOG(DEBUG, "engine '%s': failed to unregister: %s",
+						engine_ref.engine->name, unreg_error.message);
+			ci_flow_free(engine_ref, flow);
+			flow = NULL;
+			goto unlock;
+		}
+
+		CI_DRV_LOG(DEBUG, "flow installed by engine '%s'",
+				engine_ref.engine->name);
+		/* success */
+		TAILQ_INSERT_TAIL(&engine_conf->flows, flow, node);
+		goto unlock;
+	}
+
+	/* no engine could handle this flow */
+	CI_DRV_LOG(DEBUG, "no engine accepted the flow");
+	flow = NULL;
+	rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"No flow engine could handle the requested flow");
+unlock:
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return (struct rte_flow *)flow;
+}
+
+/* default implementation of rte_flow_validate using flow engines */
+static inline int
+ci_flow_validate(struct ci_flow_engine_conf *engine_conf,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error)
+{
+	struct ci_flow_engine_ref engine_ref;
+	int ret;
+
+	if (attr == NULL || actions == NULL) {
+		CI_DRV_LOG(DEBUG, "attr or actions is NULL");
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ATTR, NULL,
+				"Attributes and actions cannot be NULL");
+	}
+
+	/* lock the config for reading */
+	rte_rwlock_read_lock(&engine_conf->config_lock);
+
+	/* find an engine that can handle this flow */
+	CI_FLOW_ENGINE_LIST_FOREACH(engine_ref, engine_conf) {
+		struct ci_flow *flow;
+
+		if (!ci_flow_engine_is_enabled(engine_conf, engine_ref.engine_idx))
+			continue;
+
+		/* use OS allocator as we're not keeping the flow */
+		flow = (struct ci_flow *)calloc(1, engine_ref.engine->flow_size);
+		if (flow == NULL) {
+			/* this is a serious error so don't continue */
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to allocate flow",
+					engine_ref.engine->name);
+			ret = rte_flow_error_set(error, ENOMEM,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to allocate memory for flow rule");
+			goto unlock;
+		}
+		/* set up the flow fields */
+		flow->fallback_alloc = false;
+		flow->engine_idx = engine_ref.engine_idx;
+		flow->dev_data = engine_conf->dev_data;
+		flow->engine_priv = engine_conf->engine_priv[engine_ref.engine_idx];
+
+		/* try to parse the flow with this engine */
+		ret = ci_flow_parse(engine_conf, engine_ref.engine, attr, pattern,
+				actions, flow, error);
+		free(flow);
+
+		if (ret == 0) {
+			CI_DRV_LOG(DEBUG, "engine '%s' accepted flow",
+					engine_ref.engine->name);
+			goto unlock;
+		} else if (error != NULL) {
+			CI_DRV_LOG(DEBUG, "engine '%s' rejected flow: %s",
+					engine_ref.engine->name, error->message);
+		} else {
+			CI_DRV_LOG(DEBUG, "engine '%s' rejected flow",
+					engine_ref.engine->name);
+		}
+	}
+	/* no engine could handle this flow */
+	CI_DRV_LOG(DEBUG, "no engine accepted the flow");
+	ret = rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"No flow engine could handle the requested flow");
+unlock:
+	rte_rwlock_read_unlock(&engine_conf->config_lock);
+	return ret;
+}
+
+/* default implementation of rte_flow_destroy using flow engines. */
+static inline int
+ci_flow_destroy(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow *rte_flow,
+		struct rte_flow_error *error)
+{
+	struct ci_flow *flow = (struct ci_flow *)rte_flow;
+	struct ci_flow_engine_ref engine_ref;
+	struct rte_flow_error unreg_error = {0};
+	int unreg_ret, ret = 0;
+
+	if (rte_flow == NULL) {
+		CI_DRV_LOG(DEBUG, "flow handle is NULL");
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Flow handle cannot be NULL");
+	}
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	/* validate the flow */
+	if (!ci_flow_is_valid(flow, engine_conf)) {
+		CI_DRV_LOG(DEBUG, "invalid flow handle");
+		ret = rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Invalid flow handle");
+		goto unlock;
+	}
+	engine_ref = ci_flow_engine_ref_from_idx(engine_conf, flow->engine_idx);
+	CI_DRV_LOG(DEBUG, "uninstalling flow (engine '%s')",
+			engine_ref.engine->name);
+
+	ret = ci_flow_uninstall(engine_ref, flow, error);
+
+	if (ret != 0) {
+		if (error != NULL)
+			CI_DRV_LOG(DEBUG, "uninstall failed: %s", error->message);
+		else
+			CI_DRV_LOG(DEBUG, "uninstall failed");
+		goto unlock;
+	}
+
+	/* untrack after HW removal; ignore errors on this teardown path */
+	unreg_ret = ci_flow_unregister(engine_ref, flow, &unreg_error);
+	if (unreg_ret != 0)
+		CI_DRV_LOG(DEBUG, "engine '%s': failed to unregister flow: %s",
+				engine_ref.engine->name, unreg_error.message);
+
+	/* remove the flow from the list and free it */
+	TAILQ_REMOVE(&engine_conf->flows, flow, node);
+	ci_flow_free(engine_ref, flow);
+unlock:
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return ret;
+}
+
+/* default implementation of rte_flow_flush using flow engines */
+static inline int
+ci_flow_flush(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow_error *error)
+{
+	struct ci_flow *flow, *tmp;
+
+	CI_DRV_LOG(DEBUG, "removing all flows");
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	/* iterate over all flows and uninstall them */
+	RTE_TAILQ_FOREACH_SAFE(flow, &engine_conf->flows, node, tmp) {
+		struct ci_flow_engine_ref engine_ref;
+		struct rte_flow_error unreg_error = { 0 };
+		int uninstall_ret;
+		int unreg_ret;
+
+		/* this shouldn't happen */
+		if (!ci_flow_is_valid(flow, engine_conf))
+			continue;
+
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf,
+				flow->engine_idx);
+
+		uninstall_ret = ci_flow_uninstall(engine_ref, flow, error);
+
+		/* if uninstall failed, log but ignore failure */
+		if (uninstall_ret != 0) {
+			if (error != NULL)
+				CI_DRV_LOG(DEBUG, "engine '%s': failed to uninstall flow: %s",
+						engine_ref.engine->name, error->message);
+			else
+				CI_DRV_LOG(DEBUG, "engine '%s': failed to uninstall flow",
+						engine_ref.engine->name);
+		}
+
+		/* untrack after HW removal; ignore errors on this teardown path, but log them */
+		unreg_ret = ci_flow_unregister(engine_ref, flow, &unreg_error);
+		if (unreg_ret != 0)
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to untrack flow: %s",
+					engine_ref.engine->name, unreg_error.message);
+
+		TAILQ_REMOVE(&engine_conf->flows, flow, node);
+		ci_flow_free(engine_ref, flow);
+	}
+
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return 0;
+}
+
+/* default implementation of rte_flow_query using flow engines */
+static inline int
+ci_flow_query(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow *rte_flow,
+		const struct rte_flow_action *action,
+		void *data,
+		struct rte_flow_error *error)
+{
+	struct ci_flow *flow = (struct ci_flow *)rte_flow;
+	struct ci_flow_engine_ref engine_ref;
+	int ret;
+
+	if (action == NULL || data == NULL) {
+		CI_DRV_LOG(DEBUG, "action or data is NULL");
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Action or data cannot be NULL");
+	}
+
+	/* lock the config for reading */
+	rte_rwlock_read_lock(&engine_conf->config_lock);
+
+	/* validate the flow first */
+	if (!ci_flow_is_valid(flow, engine_conf)) {
+		CI_DRV_LOG(DEBUG, "invalid flow handle");
+		ret = rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"Invalid flow handle");
+		goto unlock;
+	}
+	/* find the engine that created this flow */
+	engine_ref = ci_flow_engine_ref_from_idx(engine_conf, flow->engine_idx);
+	/* query the flow if supported */
+	if (engine_ref.engine->ops->flow_query != NULL) {
+		ret = engine_ref.engine->ops->flow_query(flow, action, data, error);
+
+		if (ret != 0 && error != NULL)
+			CI_DRV_LOG(DEBUG, "engine '%s' query failed: %s",
+					engine_ref.engine->name, error->message);
+		else if (ret != 0)
+			CI_DRV_LOG(DEBUG, "engine '%s' query failed",
+					engine_ref.engine->name);
+	} else {
+		CI_DRV_LOG(DEBUG, "engine '%s' does not support querying",
+				engine_ref.engine->name);
+		ret = rte_flow_error_set(error, ENOTSUP,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+			"Flow engine does not support querying");
+	}
+unlock:
+	rte_rwlock_read_unlock(&engine_conf->config_lock);
+
+	return ret;
+}
+
+/* dump flow rule chunks using memdump - helper for ci_flow_dump */
+#define CI_FLOW_DUMP_CHUNK_BYTES 32
+
+static inline void
+ci_flow_dump_one(FILE *file, const char *driver, const char *engine,
+		const void *data, size_t data_len)
+{
+	const uint8_t *raw = (const uint8_t *)data;
+	const size_t nchunks =
+		(data_len + CI_FLOW_DUMP_CHUNK_BYTES - 1) /
+		CI_FLOW_DUMP_CHUNK_BYTES;
+	char title[64];
+	size_t ci;
+
+	fprintf(file, "FLOW DUMP: driver=%s engine=%s\n", driver, engine);
+	fprintf(file, "FLOW DUMP: DATA size=%zu chunks=%zu chunk_bytes=%d\n",
+		data_len, nchunks, CI_FLOW_DUMP_CHUNK_BYTES);
+
+	for (ci = 0; ci < nchunks; ci++) {
+		const size_t off = ci * CI_FLOW_DUMP_CHUNK_BYTES;
+		const size_t clen =
+			RTE_MIN((size_t)CI_FLOW_DUMP_CHUNK_BYTES,
+				data_len - off);
+		snprintf(title, sizeof(title), "FLOW DUMP: chunk %03zu/%03zu",
+			 ci + 1, nchunks);
+		rte_memdump(file, title, raw + off, clen);
+	}
+}
+
+/* default implementation of rte_flow_dev_dump using flow engines */
+static inline int
+ci_flow_dump(struct ci_flow_engine_conf *engine_conf,
+		struct rte_flow *flow,
+		FILE *file,
+		struct rte_flow_error *error)
+{
+	struct ci_flow_engine_ref engine_ref;
+	struct ci_flow *cur;
+	bool found = false;
+	const char *driver_name =
+#ifdef RTE_COMPONENT_NAME
+			RTE_STR(RTE_COMPONENT_NAME);
+#else
+			"unknown";
+#endif
+
+	rte_rwlock_read_lock(&engine_conf->config_lock);
+
+	TAILQ_FOREACH(cur, &engine_conf->flows, node) {
+		const void *data;
+		size_t data_len;
+
+		if (flow != NULL && (struct rte_flow *)cur != flow)
+			continue;
+
+		/* is this flow valid? */
+		if (!ci_flow_is_valid(cur, engine_conf)) {
+			CI_DRV_LOG(DEBUG, "invalid flow handle: %p, skipping", cur);
+			continue;
+		}
+
+		found = true;
+
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf, cur->engine_idx);
+
+		data = RTE_PTR_ADD(cur, sizeof(struct ci_flow));
+		data_len = engine_ref.engine->flow_size - sizeof(struct ci_flow);
+
+		/*
+		 * when data_len is 0 the dump loop would not access the data
+		 * pointer, but static analysis tools may flag this as a
+		 * potential NULL dereference, so skip dump when data_len is 0.
+		 */
+		if (data_len == 0) {
+			CI_DRV_LOG(DEBUG, "flow data length is 0, skipping dump");
+			continue;
+		}
+
+		ci_flow_dump_one(file, driver_name, engine_ref.engine->name,
+				data, data_len);
+	}
+
+	rte_rwlock_read_unlock(&engine_conf->config_lock);
+
+	if (flow != NULL && !found) {
+		return rte_flow_error_set(error, ENOENT,
+			RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+			"Flow not found");
+	}
+
+	return 0;
+}
+
+/*
+ * The following functions are designed to be called from the context of
+ * the driver lifecycle functions such as `dev_start`.
+ *
+ * These default implementations take the config lock internally.
+ */
+
+/* re-install all flows */
+static inline int
+ci_flow_replay(struct ci_flow_engine_conf *engine_conf)
+{
+	struct rte_flow_error error = {0};
+	struct ci_flow *flow;
+
+	CI_DRV_LOG(DEBUG, "replaying all flows");
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	TAILQ_FOREACH(flow, &engine_conf->flows, node) {
+		struct ci_flow_engine_ref engine_ref;
+		int install_ret;
+
+		/* this shouldn't happen */
+		if (!ci_flow_is_valid(flow, engine_conf))
+			continue;
+
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf,
+				flow->engine_idx);
+
+		install_ret = ci_flow_install(engine_ref, flow, &error);
+
+		/* if install failed, log but ignore failure */
+		if (install_ret != 0) {
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to install flow: %s",
+					engine_ref.engine->name, error.message);
+		}
+	}
+
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return 0;
+}
+
+/* remove all flows without uninstalling */
+static inline int
+ci_flow_cleanup(struct ci_flow_engine_conf *engine_conf)
+{
+	struct rte_flow_error error = {0};
+	struct ci_flow *flow, *tmp;
+
+	CI_DRV_LOG(DEBUG, "cleaning up all flows");
+
+	/* lock the config for writing */
+	rte_rwlock_write_lock(&engine_conf->config_lock);
+
+	RTE_TAILQ_FOREACH_SAFE(flow, &engine_conf->flows, node, tmp) {
+		struct ci_flow_engine_ref engine_ref;
+		int unregister_ret;
+
+		/* this shouldn't happen */
+		if (!ci_flow_is_valid(flow, engine_conf))
+			continue;
+
+		engine_ref = ci_flow_engine_ref_from_idx(engine_conf,
+				flow->engine_idx);
+
+		/* untrack only; the hardware is not touched */
+		unregister_ret = ci_flow_unregister(engine_ref, flow, &error);
+
+		/* if untrack failed, log but ignore failure */
+		if (unregister_ret != 0) {
+			CI_DRV_LOG(DEBUG, "engine '%s': failed to untrack flow: %s",
+					engine_ref.engine->name, error.message);
+		}
+
+		TAILQ_REMOVE(&engine_conf->flows, flow, node);
+		ci_flow_free(engine_ref, flow);
+	}
+
+	rte_rwlock_write_unlock(&engine_conf->config_lock);
+
+	return 0;
+}
+
+#endif /* _COMMON_INTEL_FLOW_ENGINE_H_ */
Signature · 1 line
2.52.0
#34 lore raw

PATCH v2 3/19 net/intel/common: add utility functions

In reply to Anatoly Burakov

A lot of parsers will rely on doing the same things over and over, so create a header with utility functions to aid in writing parsers.

---
 drivers/net/intel/common/flow_util.h | 183 +++++++++++++++++++++++++++
 1 file changed, 183 insertions(+)
 create mode 100644 drivers/net/intel/common/flow_util.h
drivers/net/intel/common/flow_util.h new file +183 -0
new file mode 100644
index 0000000000..57f12c4637
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _COMMON_INTEL_FLOW_UTIL_H_
+#define _COMMON_INTEL_FLOW_UTIL_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * Utility functions primarily intended for flow parsers.
+ */
+
+/**
+ * Check if memory region is filled with a specific byte value.
+ *
+ * @param ptr
+ *   Pointer to memory region.
+ * @param len
+ *   Length in bytes.
+ * @param val
+ *   Byte value to check (e.g. 0x00 or 0xFF).
+ * @return
+ *   true if all bytes equal val, false otherwise.
+ */
+static inline bool
+ci_is_all_byte(const void *ptr, size_t len, uint8_t val)
+{
+	const uint8_t *bytes = (const uint8_t *)ptr;
+	const uint32_t pattern32 = 0x01010101U * val;
+	size_t i = 0;
+
+	/* Process 4-byte chunks using memcpy */
+	for (; i + 4 <= len; i += 4) {
+		uint32_t chunk;
+		memcpy(&chunk, bytes + i, 4);
+		if (chunk != pattern32)
+			return false;
+	}
+
+	/* Process remaining bytes */
+	for (; i < len; i++) {
+		if (bytes[i] != val)
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * Check if bytes are all 0x00 OR all 0xFF.
+ *
+ * @param ptr
+ *   Pointer to memory region.
+ * @param len
+ *   Length in bytes.
+ * @return
+ *   true if all bytes are 0x00 OR all bytes are 0xFF, false otherwise.
+ */
+static inline bool
+ci_is_all_zero_or_masked(const void *ptr, size_t len)
+{
+	const uint8_t *bytes = (const uint8_t *)ptr;
+	uint8_t first_val;
+
+	/* zero length cannot be valid */
+	if (len == 0)
+		return false;
+
+	first_val = bytes[0];
+
+	if (first_val != 0x00 && first_val != 0xFF)
+		return false;
+
+	return ci_is_all_byte(ptr, len, first_val);
+}
+
+/**
+ * Check if a value has no bits outside the mask, and within the mask is
+ * either all-zero or all-one.
+ *
+ * This is intended for bitfields e.g. VLAN_TCI. For byte-aligned fields,
+ * use CI_FIELD_IS_ZERO_OR_MASKED below.
+ *
+ * @param value
+ *   Data value to check.
+ * @param mask
+ *   Mask to compare against.
+ * @return
+ *   true if (value & ~mask) == 0 AND (value & mask) is 0 or mask,
+ *   false otherwise.
+ */
+static inline bool
+ci_is_zero_or_masked(uint64_t value, uint64_t mask)
+{
+	uint64_t masked = value & mask;
+	uint64_t unmasked = value & ~mask;
+
+	return unmasked == 0 && (masked == 0 || masked == mask);
+}
+
+/**
+ * Check if a struct field is fully masked or unmasked.
+ *
+ * @param field_ptr
+ *   Pointer to the mask field (e.g. &eth_mask->hdr.src_addr).
+ */
+#define CI_FIELD_IS_ZERO_OR_MASKED(field_ptr) \
+	ci_is_all_zero_or_masked((field_ptr), sizeof(*(field_ptr)))
+
+/**
+ * Check if a struct field is all 0x00.
+ *
+ * @param field_ptr
+ *   Pointer to the mask field (e.g. &eth_mask->hdr.src_addr).
+ */
+#define CI_FIELD_IS_ZERO(field_ptr) \
+	ci_is_all_byte((field_ptr), sizeof(*(field_ptr)), 0x00)
+
+/**
+ * Check if a struct field is all 0xFF.
+ *
+ * @param field_ptr
+ *   Pointer to the mask field (e.g. &eth_mask->hdr.src_addr).
+ */
+#define CI_FIELD_IS_MASKED(field_ptr) \
+	ci_is_all_byte((field_ptr), sizeof(*(field_ptr)), 0xFF)
+
+/**
+ * Convert 24-bit big-endian value to host byte order.
+ *
+ * Used to extract 24-bit big-endian values (e.g. VXLAN VNI).
+ *
+ * @param val
+ *   Pointer to 3-byte big-endian value.
+ * @return
+ *   Value in host byte order.
+ */
+static inline uint32_t
+ci_be24_to_cpu(const uint8_t val[3])
+{
+	return (val[0] << 16) | (val[1] << 8) | val[2];
+}
+
+/**
+ * Check if a character is a valid hexadecimal digit.
+ *
+ * @param c
+ *   Character to check.
+ * @return
+ *   true if c is in [0-9a-fA-F], false otherwise.
+ */
+static inline bool
+ci_is_hex_char(unsigned char c)
+{
+	return ((c >= '0' && c <= '9') ||
+		(c >= 'a' && c <= 'f') ||
+		(c >= 'A' && c <= 'F'));
+}
+
+/**
+ * Convert hex character to 4-bit value.
+ *
+ * @param c
+ *   Hex character ('0'-'9', 'a'-'f', 'A'-'F').
+ * @return
+ *   Value 0-15, or 0 if invalid.
+ */
+static inline unsigned char
+ci_hex_char_to_nibble(unsigned char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	return 0;
+}
+
+#endif /* _INTEL_COMMON_FLOW_UTIL_H_ */
Signature · 1 line
2.52.0
#35 lore raw

PATCH v2 4/19 net/ixgbe: add support for common flow parsing

In reply to Anatoly Burakov

Implement support for common flow parsing infrastructure in preparation for migration of flow engines. The following features are enabled:

- Conf init on dev_init
- Conf uninit on dev_close
- Flow cleanup on dev_close
- Flow replay on dev_start

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c | 22 ++++++++++-
 drivers/net/intel/ixgbe/ixgbe_ethdev.h |  5 +++
 drivers/net/intel/ixgbe/ixgbe_flow.c   | 54 +++++++++++++++++++++++++-
 drivers/net/intel/ixgbe/ixgbe_flow.h   | 12 ++++++
 4 files changed, 90 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow.h
drivers/net/intel/ixgbe/ixgbe_ethdev.c +20 -2
index 4b7f51ce13..20807534cd 100644
@@ -46,6 +46,7 @@
 #include "base/ixgbe_phy.h"
 #include "ixgbe_osdep.h"
 #include "ixgbe_regs.h"
+#include "ixgbe_flow.h"
 
 /*
  * High threshold controlling when to start sending XOFF frames. Must be at
@@ -1334,6 +1335,12 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 	if (ret)
 		goto err_l2_tn_filter_init;
 
+	/* initialize flow engine configuration */
+	ret = ci_flow_engine_conf_init(&ad->flow_engine_conf,
+			&ixgbe_flow_engine_list, eth_dev->data);
+	if (ret)
+		goto err_flow_engine_conf_init;
+
 	/* initialize flow filter lists */
 	ixgbe_filterlist_init(eth_dev);
 
@@ -1345,6 +1352,8 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 
 	return 0;
 
+err_flow_engine_conf_init:
+	ixgbe_l2_tn_filter_uninit(eth_dev);
 err_l2_tn_filter_init:
 	ixgbe_fdir_filter_uninit(eth_dev);
 err_fdir_filter_init:
@@ -2910,6 +2919,9 @@ ixgbe_dev_start(struct rte_eth_dev *dev)
 	if (macsec_setting->offload_en)
 		ixgbe_dev_macsec_register_enable(dev, macsec_setting);
 
+	/* re-install kept flows to hardware */
+	ci_flow_replay(&adapter->flow_engine_conf);
+
 	return 0;
 
 error:
@@ -3089,8 +3101,8 @@ ixgbe_dev_set_link_down(struct rte_eth_dev *dev)
 static int
 ixgbe_dev_close(struct rte_eth_dev *dev)
 {
-	struct ixgbe_hw *hw =
-		IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *ad = dev->data->dev_private;
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(ad);
 	struct rte_pci_device *pci_dev = RTE_CLASS_TO_BUS_DEVICE(dev, *pci_dev);
 	struct rte_intr_handle *intr_handle = pci_dev->intr_handle;
 	int retries = 0;
@@ -3157,6 +3169,12 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	rte_free(dev->security_ctx);
 	dev->security_ctx = NULL;
 
+	/* drop all flows */
+	ci_flow_cleanup(&ad->flow_engine_conf);
+
+	/* reset flow engines */
+	ci_flow_engine_conf_reset(&ad->flow_engine_conf);
+
 	return ret;
 }
 
drivers/net/intel/ixgbe/ixgbe_ethdev.h +5 -0
index 5d3243cb4d..cde0ee8fda 100644
@@ -22,6 +22,8 @@
 #include <bus_pci_driver.h>
 #include <rte_tm_driver.h>
 
+#include "../common/flow_engine.h"
+
 /* need update link, bit flag */
 #define IXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
 #define IXGBE_FLAG_MAILBOX          (uint32_t)(1 << 1)
@@ -346,6 +348,7 @@ struct ixgbe_l2_tn_info {
 };
 
 struct rte_flow {
+	struct ci_flow flow;
 	enum rte_filter_type filter_type;
 	/* security flows are not rte_filter_type */
 	bool is_security;
@@ -492,6 +495,8 @@ struct ixgbe_adapter {
 	struct rte_timecounter      tx_tstamp_tc;
  	struct ixgbe_tm_conf        tm_conf;
 
+	struct ci_flow_engine_conf flow_engine_conf;
+
 	/* For RSS reta table update */
 	uint8_t rss_reta_updated;
 
drivers/net/intel/ixgbe/ixgbe_flow.c +53 -1

104 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h new file +12 -0
new file mode 100644
index 0000000000..5e68c9886c
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef _IXGBE_FLOW_H_
+#define _IXGBE_FLOW_H_
+
+#include "../common/flow_engine.h"
+
+extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
+
+#endif /*  _IXGBE_FLOW_H_ */
Signature · 1 line
2.52.0
#36 lore raw

PATCH v2 5/19 net/ixgbe: reimplement ethertype parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for ethertype.

The old ethertype parser was accepting certain things that were later rejected by the actual ethertype installation code, in particular DROP action as well as dst MAC address filtering. This was removed from the graph parser.

The ethertype filter tracking table is used by the rte_flow ethertype engine, but it is also in use by other features, so the filter tracking is refactored to be properly shared between the engine and other features that write into the same table.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c        | 186 +++++------
 drivers/net/intel/ixgbe/ixgbe_ethdev.h        |  89 ++----
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 219 +------------
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   8 +
 .../net/intel/ixgbe/ixgbe_flow_ethertype.c    | 295 ++++++++++++++++++
 drivers/net/intel/ixgbe/ixgbe_pf.c            |  49 +--
 drivers/net/intel/ixgbe/meson.build           |   1 +
 7 files changed, 429 insertions(+), 418 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ethertype.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +71 -115
index 20807534cd..8cd57c1ee7 100644
@@ -2961,6 +2961,10 @@ ixgbe_dev_stop(struct rte_eth_dev *dev)
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
 		return -E_RTE_SECONDARY;
 
+	/* disable timestamping; the application must re-enable it after restart */
+	if (adapter->filter.timesync_installed)
+		ixgbe_timesync_disable(dev);
+
 	ixgbe_dev_wait_setup_link_complete(dev, 0);
 
 	/* disable interrupts */
@@ -6852,76 +6856,51 @@ ixgbe_add_del_ntuple_filter(struct ixgbe_adapter *adapter,
 	return 0;
 }
 
-int
-ixgbe_add_del_ethertype_filter(struct ixgbe_adapter *adapter,
-			struct rte_eth_ethertype_filter *filter,
-			bool add)
+void
+ixgbe_ethertype_filter_program(struct ixgbe_hw *hw, uint8_t idx,
+			uint32_t etqf, uint32_t etqs)
 {
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(adapter);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(adapter);
-	uint32_t etqf = 0;
-	uint32_t etqs = 0;
-	int ret;
-	struct ixgbe_ethertype_filter ethertype_filter;
-
-	if (filter->queue >= IXGBE_MAX_RX_QUEUE_NUM)
-		return -EINVAL;
-
-	if (filter->ether_type == RTE_ETHER_TYPE_IPV4 ||
-		filter->ether_type == RTE_ETHER_TYPE_IPV6) {
-		PMD_DRV_LOG(ERR, "unsupported ether_type(0x%04x) in"
-			" ethertype filter.", filter->ether_type);
-		return -EINVAL;
-	}
-
-	if (filter->flags & RTE_ETHTYPE_FLAGS_MAC) {
-		PMD_DRV_LOG(ERR, "mac compare is unsupported.");
-		return -EINVAL;
-	}
-	if (filter->flags & RTE_ETHTYPE_FLAGS_DROP) {
-		PMD_DRV_LOG(ERR, "drop option is unsupported.");
-		return -EINVAL;
-	}
-
-	ret = ixgbe_ethertype_filter_lookup(filter_info, filter->ether_type);
-	if (ret >= 0 && add) {
-		PMD_DRV_LOG(ERR, "ethertype (0x%04x) filter exists.",
-			    filter->ether_type);
-		return -EEXIST;
-	}
-	if (ret < 0 && !add) {
-		PMD_DRV_LOG(ERR, "ethertype (0x%04x) filter doesn't exist.",
-			    filter->ether_type);
-		return -ENOENT;
-	}
-
-	if (add) {
-		etqf = IXGBE_ETQF_FILTER_EN;
-		etqf |= (uint32_t)filter->ether_type;
-		etqs |= (uint32_t)((filter->queue <<
-				    IXGBE_ETQS_RX_QUEUE_SHIFT) &
-				    IXGBE_ETQS_RX_QUEUE);
-		etqs |= IXGBE_ETQS_QUEUE_EN;
-
-		ethertype_filter.ethertype = filter->ether_type;
-		ethertype_filter.etqf = etqf;
-		ethertype_filter.etqs = etqs;
-		ethertype_filter.conf = FALSE;
-		ret = ixgbe_ethertype_filter_insert(filter_info,
-						    &ethertype_filter);
-		if (ret < 0) {
-			PMD_DRV_LOG(ERR, "ethertype filters are full.");
-			return -ENOSPC;
-		}
-	} else {
-		ret = ixgbe_ethertype_filter_remove(filter_info, (uint8_t)ret);
-		if (ret < 0)
-			return -ENOSYS;
-	}
-	IXGBE_WRITE_REG(hw, IXGBE_ETQF(ret), etqf);
-	IXGBE_WRITE_REG(hw, IXGBE_ETQS(ret), etqs);
+	IXGBE_WRITE_REG(hw, IXGBE_ETQF(idx), etqf);
+	IXGBE_WRITE_REG(hw, IXGBE_ETQS(idx), etqs);
 	IXGBE_WRITE_FLUSH(hw);
+}
+
+int
+ixgbe_ethertype_table_add(struct ixgbe_ethertype_table *table,
+			uint16_t ethertype, uint32_t etqf, uint32_t etqs)
+{
+	int free_idx = -1;
+	int i;
+
+	for (i = 0; i < IXGBE_MAX_ETQF_FILTERS; i++) {
+		if (table->mask & (1u << i)) {
+			if (table->entries[i].ethertype == ethertype)
+				return -EEXIST;
+		} else if (free_idx < 0) {
+			free_idx = i;
+		}
+	}
+	if (free_idx < 0)
+		return -ENOSPC;
+
+	table->mask |= 1u << free_idx;
+	table->entries[free_idx].ethertype = ethertype;
+	table->entries[free_idx].etqf = etqf;
+	table->entries[free_idx].etqs = etqs;
+
+	return free_idx;
+}
+
+int
+ixgbe_ethertype_table_del(struct ixgbe_ethertype_table *table, uint8_t idx)
+{
+	if (idx >= IXGBE_MAX_ETQF_FILTERS || !(table->mask & (1u << idx)))
+		return -ENOENT;
+
+	table->mask &= ~(1u << idx);
+	table->entries[idx].ethertype = 0;
+	table->entries[idx].etqf = 0;
+	table->entries[idx].etqs = 0;
 
 	return 0;
 }
@@ -7159,8 +7138,11 @@ static int
 ixgbe_timesync_enable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_filter_info *filter_info =
+		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
 	uint32_t tsync_ctl;
 	uint32_t tsauxc;
+	uint32_t etqf;
 	struct timespec ts;
 
 	memset(&ts, 0, sizeof(struct timespec));
@@ -7182,10 +7164,19 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev)
 	ixgbe_start_timecounters(dev);
 
 	/* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
-	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
-			(RTE_ETHER_TYPE_1588 |
-			 IXGBE_ETQF_FILTER_EN |
-			 IXGBE_ETQF_1588));
+	etqf = RTE_ETHER_TYPE_1588 | IXGBE_ETQF_FILTER_EN | IXGBE_ETQF_1588;
+	if (!filter_info->timesync_installed) {
+		int idx = ixgbe_ethertype_table_add(&filter_info->ethertype_table,
+				RTE_ETHER_TYPE_1588, etqf, 0);
+
+		if (idx < 0) {
+			PMD_DRV_LOG(ERR, "no free ETQF slot for 1588 timestamping");
+			return idx;
+		}
+		filter_info->timesync_idx = idx;
+		filter_info->timesync_installed = true;
+	}
+	ixgbe_ethertype_filter_program(hw, filter_info->timesync_idx, etqf, 0);
 
 	/* Enable timestamping of received PTP packets. */
 	tsync_ctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
@@ -7209,6 +7200,8 @@ static int
 ixgbe_timesync_disable(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_filter_info *filter_info =
+		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
 	uint32_t tsync_ctl;
 
 	/* Disable timestamping of transmitted PTP packets. */
@@ -7222,7 +7215,12 @@ ixgbe_timesync_disable(struct rte_eth_dev *dev)
 	IXGBE_WRITE_REG(hw, IXGBE_TSYNCRXCTL, tsync_ctl);
 
 	/* Disable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */
-	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588), 0);
+	if (filter_info->timesync_installed) {
+		ixgbe_ethertype_filter_program(hw, filter_info->timesync_idx, 0, 0);
+		ixgbe_ethertype_table_del(&filter_info->ethertype_table,
+				filter_info->timesync_idx);
+		filter_info->timesync_installed = false;
+	}
 
 	/* Stop incrementing the System Time registers. */
 	IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, 0);
@@ -8364,26 +8362,6 @@ ixgbe_ntuple_filter_restore(struct rte_eth_dev *dev)
 	}
 }
 
-/* restore ethernet type filter */
-static inline void
-ixgbe_ethertype_filter_restore(struct rte_eth_dev *dev)
-{
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
-	int i;
-
-	for (i = 0; i < IXGBE_MAX_ETQF_FILTERS; i++) {
-		if (filter_info->ethertype_mask & (1 << i)) {
-			IXGBE_WRITE_REG(hw, IXGBE_ETQF(i),
-					filter_info->ethertype_filters[i].etqf);
-			IXGBE_WRITE_REG(hw, IXGBE_ETQS(i),
-					filter_info->ethertype_filters[i].etqs);
-			IXGBE_WRITE_FLUSH(hw);
-		}
-	}
-}
-
 /* restore SYN filter */
 static inline void
 ixgbe_syn_filter_restore(struct rte_eth_dev *dev)
@@ -8439,7 +8417,6 @@ static int
 ixgbe_filter_restore(struct rte_eth_dev *dev)
 {
 	ixgbe_ntuple_filter_restore(dev);
-	ixgbe_ethertype_filter_restore(dev);
 	ixgbe_syn_filter_restore(dev);
 	ixgbe_fdir_filter_restore(dev);
 	ixgbe_l2_tn_filter_restore(dev);
@@ -8478,27 +8455,6 @@ ixgbe_clear_all_ntuple_filter(struct rte_eth_dev *dev)
 		ixgbe_remove_5tuple_filter(adapter, p_5tuple);
 }
 
-/* remove all the ether type filters */
-void
-ixgbe_clear_all_ethertype_filter(struct rte_eth_dev *dev)
-{
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
-	int i;
-
-	for (i = 0; i < IXGBE_MAX_ETQF_FILTERS; i++) {
-		if (filter_info->ethertype_mask & (1 << i) &&
-		    !filter_info->ethertype_filters[i].conf) {
-			(void)ixgbe_ethertype_filter_remove(filter_info,
-							    (uint8_t)i);
-			IXGBE_WRITE_REG(hw, IXGBE_ETQF(i), 0);
-			IXGBE_WRITE_REG(hw, IXGBE_ETQS(i), 0);
-			IXGBE_WRITE_FLUSH(hw);
-		}
-	}
-}
-
 /* remove the SYN filter */
 void
 ixgbe_clear_syn_filter(struct rte_eth_dev *dev)
drivers/net/intel/ixgbe/ixgbe_ethdev.h +22 -67

127 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.c +6 -213

295 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +8 -0
index 5e68c9886c..d7694283a5 100644
@@ -5,8 +5,16 @@
 #ifndef _IXGBE_FLOW_H_
 #define _IXGBE_FLOW_H_
 
+#include "../common/flow_check.h"
 #include "../common/flow_engine.h"
 
+int
+ixgbe_flow_actions_check(const struct ci_flow_actions *actions,
+		const struct ci_flow_actions_check_param *param,
+		struct rte_flow_error *error);
+
 extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 
+extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
+
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_ethertype.c new file +295 -0

295 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_pf.c +26 -23

76 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 0531d37acd..f2857feab7 100644
@@ -26,6 +26,7 @@ sources += files(
         'ixgbe_ethdev.c',
         'ixgbe_fdir.c',
         'ixgbe_flow.c',
+        'ixgbe_flow_ethertype.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#37 lore raw

PATCH v2 6/19 net/ixgbe: reimplement syn parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for SYN.

As a result of this migration, queue index validation has changed:

- queue is now validated at parse time against the configured number of Rx queues (nb_rx_queues), rather than at install time against the hardware maximum (IXGBE_MAX_RX_QUEUE_NUM)
- the per-function queue bound check in ixgbe_syn_filter_set() has been removed as it is no longer needed

The syn filter tracking infrastructure is moved completely inside the new engine and is removed from the rest of the driver.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c   |  70 +----
 drivers/net/intel/ixgbe/ixgbe_ethdev.h   |   7 +-
 drivers/net/intel/ixgbe/ixgbe_flow.c     | 247 +-----------------
 drivers/net/intel/ixgbe/ixgbe_flow.h     |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_syn.c | 312 +++++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build      |   1 +
 6 files changed, 319 insertions(+), 319 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_syn.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +2 -68
index 8cd57c1ee7..e4576b3844 100644
@@ -6478,43 +6478,11 @@ ixgbevf_set_default_mac_addr(struct rte_eth_dev *dev,
 	return 0;
 }
 
-int
-ixgbe_syn_filter_set(struct ixgbe_adapter *adapter,
-			struct rte_eth_syn_filter *filter,
-			bool add)
+void
+ixgbe_syn_filter_program(struct ixgbe_hw *hw, uint32_t synqf)
 {
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(adapter);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(adapter);
-	uint32_t syn_info;
-	uint32_t synqf;
-
-	if (filter->queue >= IXGBE_MAX_RX_QUEUE_NUM)
-		return -EINVAL;
-
-	syn_info = filter_info->syn_info;
-
-	if (add) {
-		if (syn_info & IXGBE_SYN_FILTER_ENABLE)
-			return -EINVAL;
-		synqf = (uint32_t)(((filter->queue << IXGBE_SYN_FILTER_QUEUE_SHIFT) &
-			IXGBE_SYN_FILTER_QUEUE) | IXGBE_SYN_FILTER_ENABLE);
-
-		if (filter->hig_pri)
-			synqf |= IXGBE_SYN_FILTER_SYNQFP;
-		else
-			synqf &= ~IXGBE_SYN_FILTER_SYNQFP;
-	} else {
-		synqf = IXGBE_READ_REG(hw, IXGBE_SYNQF);
-		if (!(syn_info & IXGBE_SYN_FILTER_ENABLE))
-			return -ENOENT;
-		synqf &= ~(IXGBE_SYN_FILTER_QUEUE | IXGBE_SYN_FILTER_ENABLE);
-	}
-
-	filter_info->syn_info = synqf;
 	IXGBE_WRITE_REG(hw, IXGBE_SYNQF, synqf);
 	IXGBE_WRITE_FLUSH(hw);
-	return 0;
 }
 
 
@@ -8362,23 +8330,6 @@ ixgbe_ntuple_filter_restore(struct rte_eth_dev *dev)
 	}
 }
 
-/* restore SYN filter */
-static inline void
-ixgbe_syn_filter_restore(struct rte_eth_dev *dev)
-{
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
-	uint32_t synqf;
-
-	synqf = filter_info->syn_info;
-
-	if (synqf & IXGBE_SYN_FILTER_ENABLE) {
-		IXGBE_WRITE_REG(hw, IXGBE_SYNQF, synqf);
-		IXGBE_WRITE_FLUSH(hw);
-	}
-}
-
 /* restore L2 tunnel filter */
 static inline void
 ixgbe_l2_tn_filter_restore(struct rte_eth_dev *dev)
@@ -8417,7 +8368,6 @@ static int
 ixgbe_filter_restore(struct rte_eth_dev *dev)
 {
 	ixgbe_ntuple_filter_restore(dev);
-	ixgbe_syn_filter_restore(dev);
 	ixgbe_fdir_filter_restore(dev);
 	ixgbe_l2_tn_filter_restore(dev);
 	ixgbe_rss_filter_restore(dev);
@@ -8455,22 +8405,6 @@ ixgbe_clear_all_ntuple_filter(struct rte_eth_dev *dev)
 		ixgbe_remove_5tuple_filter(adapter, p_5tuple);
 }
 
-/* remove the SYN filter */
-void
-ixgbe_clear_syn_filter(struct rte_eth_dev *dev)
-{
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
-
-	if (filter_info->syn_info & IXGBE_SYN_FILTER_ENABLE) {
-		filter_info->syn_info = 0;
-
-		IXGBE_WRITE_REG(hw, IXGBE_SYNQF, 0);
-		IXGBE_WRITE_FLUSH(hw);
-	}
-}
-
 /* remove all the L2 tunnel filters */
 int
 ixgbe_clear_all_l2_tn_filter(struct rte_eth_dev *dev)
drivers/net/intel/ixgbe/ixgbe_ethdev.h +1 -6
index 3f15b5a0c9..59d58f6160 100644
@@ -315,8 +315,6 @@ struct ixgbe_filter_info {
 	/* Bit mask for every used 5tuple filter */
 	uint32_t fivetuple_mask[IXGBE_5TUPLE_ARRAY_SIZE];
 	struct ixgbe_5tuple_filter_list fivetuple_list;
-	/* store the SYN filter info */
-	uint32_t syn_info;
 	/* store the rss filter info */
 	struct ixgbe_rte_flow_rss_conf rss_info;
 	/* shared EtherType (ETQF) slot table */
@@ -684,15 +682,13 @@ bool ixgbe_rss_update_sp(enum ixgbe_mac_type mac_type);
 int ixgbe_add_del_ntuple_filter(struct ixgbe_adapter *adapter,
 			struct rte_eth_ntuple_filter *filter,
 			bool add);
-int ixgbe_syn_filter_set(struct ixgbe_adapter *adapter,
-			struct rte_eth_syn_filter *filter,
-			bool add);
 
 void ixgbe_ethertype_filter_program(struct ixgbe_hw *hw, uint8_t idx,
 			uint32_t etqf, uint32_t etqs);
 int ixgbe_ethertype_table_add(struct ixgbe_ethertype_table *table,
 			uint16_t ethertype, uint32_t etqf, uint32_t etqs);
 int ixgbe_ethertype_table_del(struct ixgbe_ethertype_table *table, uint8_t idx);
+void ixgbe_syn_filter_program(struct ixgbe_hw *hw, uint32_t synqf);
 
 /**
  * l2 tunnel configuration.
@@ -765,7 +761,6 @@ int ixgbe_clear_all_fdir_filter(struct rte_eth_dev *dev);
 extern const struct rte_flow_ops ixgbe_flow_ops;
 
 void ixgbe_clear_all_ntuple_filter(struct rte_eth_dev *dev);
-void ixgbe_clear_syn_filter(struct rte_eth_dev *dev);
 int ixgbe_clear_all_l2_tn_filter(struct rte_eth_dev *dev);
 
 int ixgbe_disable_sec_tx_path_generic(struct ixgbe_hw *hw);
drivers/net/intel/ixgbe/ixgbe_flow.c +2 -245

316 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index d7694283a5..453a23d3b6 100644
@@ -16,5 +16,6 @@ ixgbe_flow_actions_check(const struct ci_flow_actions *actions,
 extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 
 extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
+extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_syn.c new file +312 -0

312 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index f2857feab7..1ef818fa67 100644
@@ -27,6 +27,7 @@ sources += files(
         'ixgbe_fdir.c',
         'ixgbe_flow.c',
         'ixgbe_flow_ethertype.c',
+        'ixgbe_flow_syn.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#38 lore raw

PATCH v2 7/19 net/ixgbe: reimplement L2 tunnel parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for L2 tunnel.

There are two L2-tag-related features in the driver: the global per-port one, and the one that can direct traffic to specific queues (the one the engine targets). The former is left completely untouched, while the latter one is migrated to use engine infrastructure and use internal tracking.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c     | 248 +---------------
 drivers/net/intel/ixgbe/ixgbe_ethdev.h     |  26 +-
 drivers/net/intel/ixgbe/ixgbe_flow.c       | 206 +------------
 drivers/net/intel/ixgbe/ixgbe_flow.h       |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c | 317 +++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build        |   1 +
 6 files changed, 326 insertions(+), 473 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +2 -246
index e4576b3844..63df23f8e4 100644
@@ -147,7 +147,6 @@ static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev);
 static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_l2_tn_filter_init(struct rte_eth_dev *eth_dev);
-static int ixgbe_l2_tn_filter_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_ntuple_filter_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_dev_configure(struct rte_eth_dev *dev);
 static int ixgbe_dev_start(struct rte_eth_dev *dev);
@@ -1353,7 +1352,6 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 	return 0;
 
 err_flow_engine_conf_init:
-	ixgbe_l2_tn_filter_uninit(eth_dev);
 err_l2_tn_filter_init:
 	ixgbe_fdir_filter_uninit(eth_dev);
 err_fdir_filter_init:
@@ -1423,25 +1421,6 @@ static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev)
 	return 0;
 }
 
-static int ixgbe_l2_tn_filter_uninit(struct rte_eth_dev *eth_dev)
-{
-	struct ixgbe_l2_tn_info *l2_tn_info =
-		IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(eth_dev->data->dev_private);
-	struct ixgbe_l2_tn_filter *l2_tn_filter;
-
-	rte_free(l2_tn_info->hash_map);
-	rte_hash_free(l2_tn_info->hash_handle);
-
-	while ((l2_tn_filter = TAILQ_FIRST(&l2_tn_info->l2_tn_list))) {
-		TAILQ_REMOVE(&l2_tn_info->l2_tn_list,
-			     l2_tn_filter,
-			     entries);
-		rte_free(l2_tn_filter);
-	}
-
-	return 0;
-}
-
 static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev)
 {
 	struct ixgbe_hw_fdir_info *fdir_info =
@@ -1487,34 +1466,7 @@ static int ixgbe_l2_tn_filter_init(struct rte_eth_dev *eth_dev)
 {
 	struct ixgbe_l2_tn_info *l2_tn_info =
 		IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(eth_dev->data->dev_private);
-	char l2_tn_hash_name[RTE_HASH_NAMESIZE];
-	struct rte_hash_parameters l2_tn_hash_params = {
-		.name = l2_tn_hash_name,
-		.entries = IXGBE_MAX_L2_TN_FILTER_NUM,
-		.key_len = sizeof(struct ixgbe_l2_tn_key),
-		.hash_func = rte_hash_crc,
-		.hash_func_init_val = 0,
-		.socket_id = rte_socket_id(),
-	};
 
-	TAILQ_INIT(&l2_tn_info->l2_tn_list);
-	snprintf(l2_tn_hash_name, RTE_HASH_NAMESIZE,
-		 "l2_tn_%s", eth_dev->device->name);
-	l2_tn_info->hash_handle = rte_hash_create(&l2_tn_hash_params);
-	if (!l2_tn_info->hash_handle) {
-		PMD_INIT_LOG(ERR, "Failed to create L2 TN hash table!");
-		return -EINVAL;
-	}
-	l2_tn_info->hash_map = rte_zmalloc("ixgbe",
-				   sizeof(struct ixgbe_l2_tn_filter *) *
-				   IXGBE_MAX_L2_TN_FILTER_NUM,
-				   0);
-	if (!l2_tn_info->hash_map) {
-		PMD_INIT_LOG(ERR,
-			"Failed to allocate memory for L2 TN hash map!");
-		rte_hash_free(l2_tn_info->hash_handle);
-		return -ENOMEM;
-	}
 	l2_tn_info->e_tag_en = FALSE;
 	l2_tn_info->e_tag_fwd_en = FALSE;
 	l2_tn_info->e_tag_ether_type = RTE_ETHER_TYPE_ETAG;
@@ -3158,9 +3110,6 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	/* remove all the fdir filters & hash */
 	ixgbe_fdir_filter_uninit(dev);
 
-	/* remove all the L2 tunnel filters & hash */
-	ixgbe_l2_tn_filter_uninit(dev);
-
 	/* Remove all ntuple filters of the device */
 	ixgbe_ntuple_filter_uninit(dev);
 
@@ -7706,7 +7655,7 @@ ixgbe_e_tag_enable(struct ixgbe_hw *hw)
 	return 0;
 }
 
-static int
+int
 ixgbe_e_tag_filter_del(struct ixgbe_adapter *adapter,
 		       struct ixgbe_l2_tunnel_conf *l2_tunnel)
 {
@@ -7743,7 +7692,7 @@ ixgbe_e_tag_filter_del(struct ixgbe_adapter *adapter,
 	return ret;
 }
 
-static int
+int
 ixgbe_e_tag_filter_add(struct ixgbe_adapter *adapter,
 		       struct ixgbe_l2_tunnel_conf *l2_tunnel)
 {
@@ -7785,154 +7734,6 @@ ixgbe_e_tag_filter_add(struct ixgbe_adapter *adapter,
 	return -EINVAL;
 }
 
-static inline struct ixgbe_l2_tn_filter *
-ixgbe_l2_tn_filter_lookup(struct ixgbe_l2_tn_info *l2_tn_info,
-			  struct ixgbe_l2_tn_key *key)
-{
-	int ret;
-
-	ret = rte_hash_lookup(l2_tn_info->hash_handle, (const void *)key);
-	if (ret < 0)
-		return NULL;
-
-	return l2_tn_info->hash_map[ret];
-}
-
-static inline int
-ixgbe_insert_l2_tn_filter(struct ixgbe_l2_tn_info *l2_tn_info,
-			  struct ixgbe_l2_tn_filter *l2_tn_filter)
-{
-	int ret;
-
-	ret = rte_hash_add_key(l2_tn_info->hash_handle,
-			       &l2_tn_filter->key);
-
-	if (ret < 0) {
-		PMD_DRV_LOG(ERR,
-			    "Failed to insert L2 tunnel filter"
-			    " to hash table %d!",
-			    ret);
-		return ret;
-	}
-
-	l2_tn_info->hash_map[ret] = l2_tn_filter;
-
-	TAILQ_INSERT_TAIL(&l2_tn_info->l2_tn_list, l2_tn_filter, entries);
-
-	return 0;
-}
-
-static inline int
-ixgbe_remove_l2_tn_filter(struct ixgbe_l2_tn_info *l2_tn_info,
-			  struct ixgbe_l2_tn_key *key)
-{
-	int ret;
-	struct ixgbe_l2_tn_filter *l2_tn_filter;
-
-	ret = rte_hash_del_key(l2_tn_info->hash_handle, key);
-
-	if (ret < 0) {
-		PMD_DRV_LOG(ERR,
-			    "No such L2 tunnel filter to delete %d!",
-			    ret);
-		return ret;
-	}
-
-	l2_tn_filter = l2_tn_info->hash_map[ret];
-	l2_tn_info->hash_map[ret] = NULL;
-
-	TAILQ_REMOVE(&l2_tn_info->l2_tn_list, l2_tn_filter, entries);
-	rte_free(l2_tn_filter);
-
-	return 0;
-}
-
-/* Add l2 tunnel filter */
-int
-ixgbe_dev_l2_tunnel_filter_add(struct ixgbe_adapter *adapter,
-			       struct ixgbe_l2_tunnel_conf *l2_tunnel,
-			       bool restore)
-{
-	int ret;
-	struct ixgbe_l2_tn_info *l2_tn_info =
-		IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(adapter);
-	struct ixgbe_l2_tn_key key;
-	struct ixgbe_l2_tn_filter *node;
-
-	if (!restore) {
-		key.l2_tn_type = l2_tunnel->l2_tunnel_type;
-		key.tn_id = l2_tunnel->tunnel_id;
-
-		node = ixgbe_l2_tn_filter_lookup(l2_tn_info, &key);
-
-		if (node) {
-			PMD_DRV_LOG(ERR,
-				    "The L2 tunnel filter already exists!");
-			return -EINVAL;
-		}
-
-		node = rte_zmalloc("ixgbe_l2_tn",
-				   sizeof(struct ixgbe_l2_tn_filter),
-				   0);
-		if (!node)
-			return -ENOMEM;
-
-		memcpy(&node->key,
-				 &key,
-				 sizeof(struct ixgbe_l2_tn_key));
-		node->pool = l2_tunnel->pool;
-		ret = ixgbe_insert_l2_tn_filter(l2_tn_info, node);
-		if (ret < 0) {
-			rte_free(node);
-			return ret;
-		}
-	}
-
-	switch (l2_tunnel->l2_tunnel_type) {
-	case RTE_ETH_L2_TUNNEL_TYPE_E_TAG:
-		ret = ixgbe_e_tag_filter_add(adapter, l2_tunnel);
-		break;
-	default:
-		PMD_DRV_LOG(ERR, "Invalid tunnel type");
-		ret = -EINVAL;
-		break;
-	}
-
-	if ((!restore) && (ret < 0))
-		(void)ixgbe_remove_l2_tn_filter(l2_tn_info, &key);
-
-	return ret;
-}
-
-/* Delete l2 tunnel filter */
-int
-ixgbe_dev_l2_tunnel_filter_del(struct ixgbe_adapter *adapter,
-			       struct ixgbe_l2_tunnel_conf *l2_tunnel)
-{
-	int ret;
-	struct ixgbe_l2_tn_info *l2_tn_info =
-		IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(adapter);
-	struct ixgbe_l2_tn_key key;
-
-	key.l2_tn_type = l2_tunnel->l2_tunnel_type;
-	key.tn_id = l2_tunnel->tunnel_id;
-	ret = ixgbe_remove_l2_tn_filter(l2_tn_info, &key);
-	if (ret < 0)
-		return ret;
-
-	switch (l2_tunnel->l2_tunnel_type) {
-	case RTE_ETH_L2_TUNNEL_TYPE_E_TAG:
-		ret = ixgbe_e_tag_filter_del(adapter, l2_tunnel);
-		break;
-	default:
-		PMD_DRV_LOG(ERR, "Invalid tunnel type");
-		ret = -EINVAL;
-		break;
-	}
-
-	return ret;
-}
-
 static int
 ixgbe_e_tag_forwarding_en_dis(struct rte_eth_dev *dev, bool en)
 {
@@ -8330,26 +8131,6 @@ ixgbe_ntuple_filter_restore(struct rte_eth_dev *dev)
 	}
 }
 
-/* restore L2 tunnel filter */
-static inline void
-ixgbe_l2_tn_filter_restore(struct rte_eth_dev *dev)
-{
-	struct ixgbe_adapter *adapter =
-		IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	struct ixgbe_l2_tn_info *l2_tn_info =
-		IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(adapter);
-	struct ixgbe_l2_tn_filter *node;
-	struct ixgbe_l2_tunnel_conf l2_tn_conf;
-
-	TAILQ_FOREACH(node, &l2_tn_info->l2_tn_list, entries) {
-		l2_tn_conf.l2_tunnel_type = node->key.l2_tn_type;
-		l2_tn_conf.tunnel_id      = node->key.tn_id;
-		l2_tn_conf.pool           = node->pool;
-		(void)ixgbe_dev_l2_tunnel_filter_add(adapter,
-						     &l2_tn_conf, TRUE);
-	}
-}
-
 /* restore rss filter */
 static inline void
 ixgbe_rss_filter_restore(struct rte_eth_dev *dev)
@@ -8369,7 +8150,6 @@ ixgbe_filter_restore(struct rte_eth_dev *dev)
 {
 	ixgbe_ntuple_filter_restore(dev);
 	ixgbe_fdir_filter_restore(dev);
-	ixgbe_l2_tn_filter_restore(dev);
 	ixgbe_rss_filter_restore(dev);
 
 	return 0;
@@ -8405,30 +8185,6 @@ ixgbe_clear_all_ntuple_filter(struct rte_eth_dev *dev)
 		ixgbe_remove_5tuple_filter(adapter, p_5tuple);
 }
 
-/* remove all the L2 tunnel filters */
-int
-ixgbe_clear_all_l2_tn_filter(struct rte_eth_dev *dev)
-{
-	struct ixgbe_adapter *adapter =
-			IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	struct ixgbe_l2_tn_info *l2_tn_info =
-		IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(dev->data->dev_private);
-	struct ixgbe_l2_tn_filter *l2_tn_filter;
-	struct ixgbe_l2_tunnel_conf l2_tn_conf;
-	int ret = 0;
-
-	while ((l2_tn_filter = TAILQ_FIRST(&l2_tn_info->l2_tn_list))) {
-		l2_tn_conf.l2_tunnel_type = l2_tn_filter->key.l2_tn_type;
-		l2_tn_conf.tunnel_id      = l2_tn_filter->key.tn_id;
-		l2_tn_conf.pool           = l2_tn_filter->pool;
-		ret = ixgbe_dev_l2_tunnel_filter_del(adapter, &l2_tn_conf);
-		if (ret < 0)
-			return ret;
-	}
-
-	return 0;
-}
-
 void
 ixgbe_dev_macsec_setting_save(struct rte_eth_dev *dev,
 				struct ixgbe_macsec_setting *macsec_setting)
drivers/net/intel/ixgbe/ixgbe_ethdev.h +4 -22
index 59d58f6160..7713932236 100644
@@ -327,23 +327,7 @@ struct ixgbe_filter_info {
 	uint8_t antispoof_idx;
 };
 
-struct ixgbe_l2_tn_key {
-	enum rte_eth_tunnel_type          l2_tn_type;
-	uint32_t                          tn_id;
-};
-
-struct ixgbe_l2_tn_filter {
-	TAILQ_ENTRY(ixgbe_l2_tn_filter)    entries;
-	struct ixgbe_l2_tn_key             key;
-	uint32_t                           pool;
-};
-
-TAILQ_HEAD(ixgbe_l2_tn_filter_list, ixgbe_l2_tn_filter);
-
 struct ixgbe_l2_tn_info {
-	struct ixgbe_l2_tn_filter_list      l2_tn_list;
-	struct ixgbe_l2_tn_filter         **hash_map;
-	struct rte_hash                    *hash_handle;
 	bool e_tag_en; /* e-tag enabled */
 	bool e_tag_fwd_en; /* e-tag based forwarding enabled */
 	uint16_t e_tag_ether_type; /* ether type for e-tag */
@@ -702,12 +686,11 @@ struct ixgbe_l2_tunnel_conf {
 };
 
 int
-ixgbe_dev_l2_tunnel_filter_add(struct ixgbe_adapter *adapter,
-			       struct ixgbe_l2_tunnel_conf *l2_tunnel,
-			       bool restore);
+ixgbe_e_tag_filter_add(struct ixgbe_adapter *adapter,
+		       struct ixgbe_l2_tunnel_conf *l2_tunnel);
 int
-ixgbe_dev_l2_tunnel_filter_del(struct ixgbe_adapter *adapter,
-			       struct ixgbe_l2_tunnel_conf *l2_tunnel);
+ixgbe_e_tag_filter_del(struct ixgbe_adapter *adapter,
+		       struct ixgbe_l2_tunnel_conf *l2_tunnel);
 void ixgbe_filterlist_init(struct rte_eth_dev *dev);
 void ixgbe_filterlist_flush(struct rte_eth_dev *dev);
 /*
@@ -761,7 +744,6 @@ int ixgbe_clear_all_fdir_filter(struct rte_eth_dev *dev);
 extern const struct rte_flow_ops ixgbe_flow_ops;
 
 void ixgbe_clear_all_ntuple_filter(struct rte_eth_dev *dev);
-int ixgbe_clear_all_l2_tn_filter(struct rte_eth_dev *dev);
 
 int ixgbe_disable_sec_tx_path_generic(struct ixgbe_hw *hw);
 
drivers/net/intel/ixgbe/ixgbe_flow.c +1 -205

272 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index 453a23d3b6..ba0486b2c0 100644
@@ -17,5 +17,6 @@ extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 
 extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
 extern const struct ci_flow_engine ixgbe_syn_flow_engine;
+extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_l2tun.c new file +317 -0

317 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 1ef818fa67..91ec261154 100644
@@ -28,6 +28,7 @@ sources += files(
         'ixgbe_flow.c',
         'ixgbe_flow_ethertype.c',
         'ixgbe_flow_syn.c',
+        'ixgbe_flow_l2tun.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#39 lore raw

PATCH v2 8/19 net/ixgbe: reimplement ntuple parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for ntuple.

The 5tuple filter tracking infrastructure is moved completely inside the new engine and is removed from the rest of the driver.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c      | 348 +-----------
 drivers/net/intel/ixgbe/ixgbe_ethdev.h      |  28 +-
 drivers/net/intel/ixgbe/ixgbe_flow.c        | 463 +--------------
 drivers/net/intel/ixgbe/ixgbe_flow.h        |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c | 597 ++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build         |   1 +
 6 files changed, 627 insertions(+), 811 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +23 -325
index 63df23f8e4..f1541a1554 100644
@@ -147,7 +147,6 @@ static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev);
 static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_l2_tn_filter_init(struct rte_eth_dev *eth_dev);
-static int ixgbe_ntuple_filter_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_dev_configure(struct rte_eth_dev *dev);
 static int ixgbe_dev_start(struct rte_eth_dev *dev);
 static int ixgbe_dev_stop(struct rte_eth_dev *dev);
@@ -302,10 +301,6 @@ static int ixgbevf_add_mac_addr(struct rte_eth_dev *dev,
 static void ixgbevf_remove_mac_addr(struct rte_eth_dev *dev, uint32_t index);
 static int ixgbevf_set_default_mac_addr(struct rte_eth_dev *dev,
 					     struct rte_ether_addr *mac_addr);
-static int ixgbe_add_5tuple_filter(struct ixgbe_adapter *adapter,
-			struct ixgbe_5tuple_filter *filter);
-static void ixgbe_remove_5tuple_filter(struct ixgbe_adapter *adapter,
-			struct ixgbe_5tuple_filter *filter);
 static int ixgbe_dev_flow_ops_get(struct rte_eth_dev *dev,
 				  const struct rte_flow_ops **ops);
 static int ixgbevf_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu);
@@ -1321,9 +1316,6 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 	memset(filter_info, 0,
 	       sizeof(struct ixgbe_filter_info));
 
-	/* initialize 5tuple filter list */
-	TAILQ_INIT(&filter_info->fivetuple_list);
-
 	/* initialize flow director filter list & hash */
 	ret = ixgbe_fdir_filter_init(eth_dev);
 	if (ret)
@@ -1384,24 +1376,6 @@ eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev)
 	return 0;
 }
 
-static int ixgbe_ntuple_filter_uninit(struct rte_eth_dev *eth_dev)
-{
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(eth_dev->data->dev_private);
-	struct ixgbe_5tuple_filter *p_5tuple;
-
-	while ((p_5tuple = TAILQ_FIRST(&filter_info->fivetuple_list))) {
-		TAILQ_REMOVE(&filter_info->fivetuple_list,
-			     p_5tuple,
-			     entries);
-		rte_free(p_5tuple);
-	}
-	memset(filter_info->fivetuple_mask, 0,
-	       sizeof(uint32_t) * IXGBE_5TUPLE_ARRAY_SIZE);
-
-	return 0;
-}
-
 static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev)
 {
 	struct ixgbe_hw_fdir_info *fdir_info =
@@ -3110,9 +3084,6 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	/* remove all the fdir filters & hash */
 	ixgbe_fdir_filter_uninit(dev);
 
-	/* Remove all ntuple filters of the device */
-	ixgbe_ntuple_filter_uninit(dev);
-
 	/* clear all the filters list */
 	ixgbe_filterlist_flush(dev);
 
@@ -6434,134 +6405,50 @@ ixgbe_syn_filter_program(struct ixgbe_hw *hw, uint32_t synqf)
 	IXGBE_WRITE_FLUSH(hw);
 }
 
-
-static inline enum ixgbe_5tuple_protocol
-convert_protocol_type(uint8_t protocol_value)
+/* program a single ntuple (5-tuple) filter slot into hardware */
+void
+ixgbe_ntuple_filter_program(struct ixgbe_hw *hw, uint16_t index,
+			const struct ixgbe_5tuple_filter_info *filter_info,
+			uint16_t queue)
 {
-	if (protocol_value == IPPROTO_TCP)
-		return IXGBE_FILTER_PROTOCOL_TCP;
-	else if (protocol_value == IPPROTO_UDP)
-		return IXGBE_FILTER_PROTOCOL_UDP;
-	else if (protocol_value == IPPROTO_SCTP)
-		return IXGBE_FILTER_PROTOCOL_SCTP;
-	else
-		return IXGBE_FILTER_PROTOCOL_NONE;
-}
-
-/* inject a 5-tuple filter to HW */
-static inline void
-ixgbe_inject_5tuple_filter(struct ixgbe_adapter *adapter,
-			   struct ixgbe_5tuple_filter *filter)
-{
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(adapter);
-	int i;
 	uint32_t ftqf, sdpqf;
 	uint32_t l34timir = 0;
 	uint8_t mask = 0xff;
 
-	i = filter->index;
+	sdpqf = (uint32_t)(filter_info->dst_port << IXGBE_SDPQF_DSTPORT_SHIFT);
+	sdpqf = sdpqf | (filter_info->src_port & IXGBE_SDPQF_SRCPORT);
 
-	sdpqf = (uint32_t)(filter->filter_info.dst_port <<
-				IXGBE_SDPQF_DSTPORT_SHIFT);
-	sdpqf = sdpqf | (filter->filter_info.src_port & IXGBE_SDPQF_SRCPORT);
-
-	ftqf = (uint32_t)(filter->filter_info.proto &
-		IXGBE_FTQF_PROTOCOL_MASK);
-	ftqf |= (uint32_t)((filter->filter_info.priority &
+	ftqf = (uint32_t)(filter_info->proto & IXGBE_FTQF_PROTOCOL_MASK);
+	ftqf |= (uint32_t)((filter_info->priority &
 		IXGBE_FTQF_PRIORITY_MASK) << IXGBE_FTQF_PRIORITY_SHIFT);
-	if (filter->filter_info.src_ip_mask == 0) /* 0 means compare. */
+	if (filter_info->src_ip_mask == 0) /* 0 means compare. */
 		mask &= IXGBE_FTQF_SOURCE_ADDR_MASK;
-	if (filter->filter_info.dst_ip_mask == 0)
+	if (filter_info->dst_ip_mask == 0)
 		mask &= IXGBE_FTQF_DEST_ADDR_MASK;
-	if (filter->filter_info.src_port_mask == 0)
+	if (filter_info->src_port_mask == 0)
 		mask &= IXGBE_FTQF_SOURCE_PORT_MASK;
-	if (filter->filter_info.dst_port_mask == 0)
+	if (filter_info->dst_port_mask == 0)
 		mask &= IXGBE_FTQF_DEST_PORT_MASK;
-	if (filter->filter_info.proto_mask == 0)
+	if (filter_info->proto_mask == 0)
 		mask &= IXGBE_FTQF_PROTOCOL_COMP_MASK;
 	ftqf |= mask << IXGBE_FTQF_5TUPLE_MASK_SHIFT;
 	ftqf |= IXGBE_FTQF_POOL_MASK_EN;
 	ftqf |= IXGBE_FTQF_QUEUE_ENABLE;
 
-	IXGBE_WRITE_REG(hw, IXGBE_DAQF(i), filter->filter_info.dst_ip);
-	IXGBE_WRITE_REG(hw, IXGBE_SAQF(i), filter->filter_info.src_ip);
-	IXGBE_WRITE_REG(hw, IXGBE_SDPQF(i), sdpqf);
-	IXGBE_WRITE_REG(hw, IXGBE_FTQF(i), ftqf);
+	IXGBE_WRITE_REG(hw, IXGBE_DAQF(index), filter_info->dst_ip);
+	IXGBE_WRITE_REG(hw, IXGBE_SAQF(index), filter_info->src_ip);
+	IXGBE_WRITE_REG(hw, IXGBE_SDPQF(index), sdpqf);
+	IXGBE_WRITE_REG(hw, IXGBE_FTQF(index), ftqf);
 
 	l34timir |= IXGBE_L34T_IMIR_RESERVE;
-	l34timir |= (uint32_t)(filter->queue <<
-				IXGBE_L34T_IMIR_QUEUE_SHIFT);
-	IXGBE_WRITE_REG(hw, IXGBE_L34T_IMIR(i), l34timir);
+	l34timir |= (uint32_t)(queue << IXGBE_L34T_IMIR_QUEUE_SHIFT);
+	IXGBE_WRITE_REG(hw, IXGBE_L34T_IMIR(index), l34timir);
 }
 
-/*
- * add a 5tuple filter
- *
- * @param
- * dev: Pointer to struct rte_eth_dev.
- * index: the index the filter allocates.
- * filter: pointer to the filter that will be added.
- * rx_queue: the queue id the filter assigned to.
- *
- * @return
- *    - On success, zero.
- *    - On failure, a negative value.
- */
-static int
-ixgbe_add_5tuple_filter(struct ixgbe_adapter *adapter,
-			struct ixgbe_5tuple_filter *filter)
+/* clear a single ntuple (5-tuple) filter slot from hardware */
+void
+ixgbe_ntuple_filter_clear(struct ixgbe_hw *hw, uint16_t index)
 {
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(adapter);
-	int i, idx, shift;
-
-	/*
-	 * look for an unused 5tuple filter index,
-	 * and insert the filter to list.
-	 */
-	for (i = 0; i < IXGBE_MAX_FTQF_FILTERS; i++) {
-		idx = i / (sizeof(uint32_t) * NBBY);
-		shift = i % (sizeof(uint32_t) * NBBY);
-		if (!(filter_info->fivetuple_mask[idx] & (1 << shift))) {
-			filter_info->fivetuple_mask[idx] |= 1 << shift;
-			filter->index = i;
-			TAILQ_INSERT_TAIL(&filter_info->fivetuple_list,
-					  filter,
-					  entries);
-			break;
-		}
-	}
-	if (i >= IXGBE_MAX_FTQF_FILTERS) {
-		PMD_DRV_LOG(ERR, "5tuple filters are full.");
-		return -ENOSYS;
-	}
-
-	ixgbe_inject_5tuple_filter(adapter, filter);
-
-	return 0;
-}
-
-/*
- * remove a 5tuple filter
- *
- * @param
- * dev: Pointer to struct rte_eth_dev.
- * filter: the pointer of the filter will be removed.
- */
-static void
-ixgbe_remove_5tuple_filter(struct ixgbe_adapter *adapter,
-			struct ixgbe_5tuple_filter *filter)
-{
-	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(adapter);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(adapter);
-	uint16_t index = filter->index;
-
-	filter_info->fivetuple_mask[index / (sizeof(uint32_t) * NBBY)] &=
-				~(1 << (index % (sizeof(uint32_t) * NBBY)));
-	TAILQ_REMOVE(&filter_info->fivetuple_list, filter, entries);
-	rte_free(filter);
-
 	IXGBE_WRITE_REG(hw, IXGBE_DAQF(index), 0);
 	IXGBE_WRITE_REG(hw, IXGBE_SAQF(index), 0);
 	IXGBE_WRITE_REG(hw, IXGBE_SDPQF(index), 0);
@@ -6614,165 +6501,6 @@ ixgbevf_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
 	return 0;
 }
 
-static inline struct ixgbe_5tuple_filter *
-ixgbe_5tuple_filter_lookup(struct ixgbe_5tuple_filter_list *filter_list,
-			struct ixgbe_5tuple_filter_info *key)
-{
-	struct ixgbe_5tuple_filter *it;
-
-	TAILQ_FOREACH(it, filter_list, entries) {
-		if (memcmp(key, &it->filter_info,
-			sizeof(struct ixgbe_5tuple_filter_info)) == 0) {
-			return it;
-		}
-	}
-	return NULL;
-}
-
-/* translate elements in struct rte_eth_ntuple_filter to struct ixgbe_5tuple_filter_info*/
-static inline int
-ntuple_filter_to_5tuple(struct rte_eth_ntuple_filter *filter,
-			struct ixgbe_5tuple_filter_info *filter_info)
-{
-	if (filter->queue >= IXGBE_MAX_RX_QUEUE_NUM ||
-		filter->priority > IXGBE_5TUPLE_MAX_PRI ||
-		filter->priority < IXGBE_5TUPLE_MIN_PRI)
-		return -EINVAL;
-
-	switch (filter->dst_ip_mask) {
-	case UINT32_MAX:
-		filter_info->dst_ip_mask = 0;
-		filter_info->dst_ip = filter->dst_ip;
-		break;
-	case 0:
-		filter_info->dst_ip_mask = 1;
-		break;
-	default:
-		PMD_DRV_LOG(ERR, "invalid dst_ip mask.");
-		return -EINVAL;
-	}
-
-	switch (filter->src_ip_mask) {
-	case UINT32_MAX:
-		filter_info->src_ip_mask = 0;
-		filter_info->src_ip = filter->src_ip;
-		break;
-	case 0:
-		filter_info->src_ip_mask = 1;
-		break;
-	default:
-		PMD_DRV_LOG(ERR, "invalid src_ip mask.");
-		return -EINVAL;
-	}
-
-	switch (filter->dst_port_mask) {
-	case UINT16_MAX:
-		filter_info->dst_port_mask = 0;
-		filter_info->dst_port = filter->dst_port;
-		break;
-	case 0:
-		filter_info->dst_port_mask = 1;
-		break;
-	default:
-		PMD_DRV_LOG(ERR, "invalid dst_port mask.");
-		return -EINVAL;
-	}
-
-	switch (filter->src_port_mask) {
-	case UINT16_MAX:
-		filter_info->src_port_mask = 0;
-		filter_info->src_port = filter->src_port;
-		break;
-	case 0:
-		filter_info->src_port_mask = 1;
-		break;
-	default:
-		PMD_DRV_LOG(ERR, "invalid src_port mask.");
-		return -EINVAL;
-	}
-
-	switch (filter->proto_mask) {
-	case UINT8_MAX:
-		filter_info->proto_mask = 0;
-		filter_info->proto =
-			convert_protocol_type(filter->proto);
-		break;
-	case 0:
-		filter_info->proto_mask = 1;
-		break;
-	default:
-		PMD_DRV_LOG(ERR, "invalid protocol mask.");
-		return -EINVAL;
-	}
-
-	filter_info->priority = (uint8_t)filter->priority;
-	return 0;
-}
-
-/*
- * add or delete a ntuple filter
- *
- * @param
- * dev: Pointer to struct rte_eth_dev.
- * ntuple_filter: Pointer to struct rte_eth_ntuple_filter
- * add: if true, add filter, if false, remove filter
- *
- * @return
- *    - On success, zero.
- *    - On failure, a negative value.
- */
-int
-ixgbe_add_del_ntuple_filter(struct ixgbe_adapter *adapter,
-			struct rte_eth_ntuple_filter *ntuple_filter,
-			bool add)
-{
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(adapter);
-	struct ixgbe_5tuple_filter_info filter_5tuple;
-	struct ixgbe_5tuple_filter *filter;
-	int ret;
-
-	if (ntuple_filter->flags != RTE_5TUPLE_FLAGS) {
-		PMD_DRV_LOG(ERR, "only 5tuple is supported.");
-		return -EINVAL;
-	}
-
-	memset(&filter_5tuple, 0, sizeof(struct ixgbe_5tuple_filter_info));
-	ret = ntuple_filter_to_5tuple(ntuple_filter, &filter_5tuple);
-	if (ret < 0)
-		return ret;
-
-	filter = ixgbe_5tuple_filter_lookup(&filter_info->fivetuple_list,
-					 &filter_5tuple);
-	if (filter != NULL && add) {
-		PMD_DRV_LOG(ERR, "filter exists.");
-		return -EEXIST;
-	}
-	if (filter == NULL && !add) {
-		PMD_DRV_LOG(ERR, "filter doesn't exist.");
-		return -ENOENT;
-	}
-
-	if (add) {
-		filter = rte_zmalloc("ixgbe_5tuple_filter",
-				sizeof(struct ixgbe_5tuple_filter), 0);
-		if (filter == NULL)
-			return -ENOMEM;
-		memcpy(&filter->filter_info,
-				 &filter_5tuple,
-				 sizeof(struct ixgbe_5tuple_filter_info));
-		filter->queue = ntuple_filter->queue;
-		ret = ixgbe_add_5tuple_filter(adapter, filter);
-		if (ret < 0) {
-			rte_free(filter);
-			return ret;
-		}
-	} else
-		ixgbe_remove_5tuple_filter(adapter, filter);
-
-	return 0;
-}
-
 void
 ixgbe_ethertype_filter_program(struct ixgbe_hw *hw, uint8_t idx,
 			uint32_t etqf, uint32_t etqs)
@@ -8116,21 +7844,6 @@ int ixgbe_enable_sec_tx_path_generic(struct ixgbe_hw *hw)
 	return IXGBE_SUCCESS;
 }
 
-/* restore n-tuple filter */
-static inline void
-ixgbe_ntuple_filter_restore(struct rte_eth_dev *dev)
-{
-	struct ixgbe_adapter *adapter =
-		IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
-	struct ixgbe_5tuple_filter *node;
-
-	TAILQ_FOREACH(node, &filter_info->fivetuple_list, entries) {
-		ixgbe_inject_5tuple_filter(adapter, node);
-	}
-}
-
 /* restore rss filter */
 static inline void
 ixgbe_rss_filter_restore(struct rte_eth_dev *dev)
@@ -8148,7 +7861,6 @@ ixgbe_rss_filter_restore(struct rte_eth_dev *dev)
 static int
 ixgbe_filter_restore(struct rte_eth_dev *dev)
 {
-	ixgbe_ntuple_filter_restore(dev);
 	ixgbe_fdir_filter_restore(dev);
 	ixgbe_rss_filter_restore(dev);
 
@@ -8171,20 +7883,6 @@ ixgbe_l2_tunnel_conf(struct rte_eth_dev *dev)
 	(void)ixgbe_update_e_tag_eth_type(hw, l2_tn_info->e_tag_ether_type);
 }
 
-/* remove all the n-tuple filters */
-void
-ixgbe_clear_all_ntuple_filter(struct rte_eth_dev *dev)
-{
-	struct ixgbe_adapter *adapter =
-		IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
-	struct ixgbe_5tuple_filter *p_5tuple;
-
-	while ((p_5tuple = TAILQ_FIRST(&filter_info->fivetuple_list)))
-		ixgbe_remove_5tuple_filter(adapter, p_5tuple);
-}
-
 void
 ixgbe_dev_macsec_setting_save(struct rte_eth_dev *dev,
 				struct ixgbe_macsec_setting *macsec_setting)
drivers/net/intel/ixgbe/ixgbe_ethdev.h +4 -24
index 7713932236..11e441fbda 100644
@@ -99,8 +99,6 @@
 #define IXGBE_L34T_IMIR_LLI             0x00100000
 #define IXGBE_L34T_IMIR_QUEUE           0x0FE00000
 #define IXGBE_L34T_IMIR_QUEUE_SHIFT     21
-#define IXGBE_5TUPLE_MAX_PRI            7
-#define IXGBE_5TUPLE_MIN_PRI            1
 
 /* The overhead from MTU to max frame size. */
 #define IXGBE_ETH_OVERHEAD (RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN)
@@ -269,8 +267,6 @@ enum ixgbe_5tuple_protocol {
 	IXGBE_FILTER_PROTOCOL_NONE,
 };
 
-TAILQ_HEAD(ixgbe_5tuple_filter_list, ixgbe_5tuple_filter);
-
 struct ixgbe_5tuple_filter_info {
 	uint32_t dst_ip;
 	uint32_t src_ip;
@@ -286,18 +282,6 @@ struct ixgbe_5tuple_filter_info {
 		proto_mask:1;    /* if mask is 1b, do not compare protocol. */
 };
 
-/* 5tuple filter structure */
-struct ixgbe_5tuple_filter {
-	TAILQ_ENTRY(ixgbe_5tuple_filter) entries;
-	uint16_t index;       /* the index of 5tuple filter */
-	struct ixgbe_5tuple_filter_info filter_info;
-	uint16_t queue;       /* rx queue assigned to */
-};
-
-#define IXGBE_5TUPLE_ARRAY_SIZE \
-	(RTE_ALIGN(IXGBE_MAX_FTQF_FILTERS, (sizeof(uint32_t) * NBBY)) / \
-	 (sizeof(uint32_t) * NBBY))
-
 /* Shared EtherType (ETQF) filter table. */
 struct ixgbe_ethertype_table {
 	uint32_t mask; /* bitmask of used ETQF slots */
@@ -312,9 +296,6 @@ struct ixgbe_ethertype_table {
  * Structure to store filters' info.
  */
 struct ixgbe_filter_info {
-	/* Bit mask for every used 5tuple filter */
-	uint32_t fivetuple_mask[IXGBE_5TUPLE_ARRAY_SIZE];
-	struct ixgbe_5tuple_filter_list fivetuple_list;
 	/* store the rss filter info */
 	struct ixgbe_rte_flow_rss_conf rss_info;
 	/* shared EtherType (ETQF) slot table */
@@ -663,9 +644,10 @@ uint32_t ixgbe_rssrk_reg_get(enum ixgbe_mac_type mac_type, uint8_t i);
 
 bool ixgbe_rss_update_sp(enum ixgbe_mac_type mac_type);
 
-int ixgbe_add_del_ntuple_filter(struct ixgbe_adapter *adapter,
-			struct rte_eth_ntuple_filter *filter,
-			bool add);
+void ixgbe_ntuple_filter_program(struct ixgbe_hw *hw, uint16_t index,
+			const struct ixgbe_5tuple_filter_info *filter_info,
+			uint16_t queue);
+void ixgbe_ntuple_filter_clear(struct ixgbe_hw *hw, uint16_t index);
 
 void ixgbe_ethertype_filter_program(struct ixgbe_hw *hw, uint8_t idx,
 			uint32_t etqf, uint32_t etqs);
@@ -743,8 +725,6 @@ int ixgbe_clear_all_fdir_filter(struct rte_eth_dev *dev);
 
 extern const struct rte_flow_ops ixgbe_flow_ops;
 
-void ixgbe_clear_all_ntuple_filter(struct rte_eth_dev *dev);
-
 int ixgbe_disable_sec_tx_path_generic(struct ixgbe_hw *hw);
 
 int ixgbe_enable_sec_tx_path_generic(struct ixgbe_hw *hw);
drivers/net/intel/ixgbe/ixgbe_flow.c +1 -462

553 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index ba0486b2c0..6f082e9402 100644
@@ -18,5 +18,6 @@ extern const struct ci_flow_engine_list ixgbe_flow_engine_list;
 extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
 extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
+extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_ntuple.c new file +597 -0

597 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 91ec261154..90cc88f002 100644
@@ -29,6 +29,7 @@ sources += files(
         'ixgbe_flow_ethertype.c',
         'ixgbe_flow_syn.c',
         'ixgbe_flow_l2tun.c',
+        'ixgbe_flow_ntuple.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#40 lore raw

PATCH v2 9/19 net/ixgbe: reimplement security parser

In reply to Anatoly Burakov

Use the new flow graph API and common flow engine infrastructure to implement flow parser for security filter. As a result, flow item checks have become more stringent:

- Mask is now explicitly validated to not have unsupported items in it, when previously they were ignored
- Mask is also validated to mask src/dst addresses, as otherwise it is inconsistent with rte_flow API

Previously, security parser was a special case, now it is a first class citizen. All decryption SA tracking has been moved into the engine, as the engine is now the authoritative source of new Rx flows and the state of the Rx SA table (the Tx SA table is still up to IPsec code to manage).

Because IPsec code does not manage the Rx SA table now, a synchronization mechanism is needed to prevent IPsec code from deallocating a security session when it is still referenced by security flows, so the security session is now atomically refcounted. For Tx, the refcount is effectively a noop, whereas for Rx it is now managed by rte_flow security engine.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.h        |   2 -
 drivers/net/intel/ixgbe/ixgbe_flow.c          | 120 +---
 drivers/net/intel/ixgbe/ixgbe_flow.h          |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_security.c | 537 ++++++++++++++++++
 drivers/net/intel/ixgbe/ixgbe_ipsec.c         | 359 ++++++------
 drivers/net/intel/ixgbe/ixgbe_ipsec.h         |  50 +-
 drivers/net/intel/ixgbe/meson.build           |   1 +
 7 files changed, 723 insertions(+), 347 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_security.c
drivers/net/intel/ixgbe/ixgbe_ethdev.h +0 -2
index 11e441fbda..bd66d4afd9 100644
@@ -317,8 +317,6 @@ struct ixgbe_l2_tn_info {
 struct rte_flow {
 	struct ci_flow flow;
 	enum rte_filter_type filter_type;
-	/* security flows are not rte_filter_type */
-	bool is_security;
 	void *rule;
 };
 
drivers/net/intel/ixgbe/ixgbe_flow.c +2 -118

174 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index 6f082e9402..87cf028245 100644
@@ -19,5 +19,6 @@ extern const struct ci_flow_engine ixgbe_ethertype_flow_engine;
 extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
 extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
+extern const struct ci_flow_engine ixgbe_security_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_security.c new file +537 -0

537 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_ipsec.c +160 -199

443 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_ipsec.h +22 -28

91 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 90cc88f002..12ba639b70 100644
@@ -30,6 +30,7 @@ sources += files(
         'ixgbe_flow_syn.c',
         'ixgbe_flow_l2tun.c',
         'ixgbe_flow_ntuple.c',
+        'ixgbe_flow_security.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#41 lore raw

PATCH v2 10/19 net/ixgbe: reimplement FDIR parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for flow director.

The FDIR flow tracking is moved inside the new engine, and the FDIR code is refactored to not mix software tracking with HW writes.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c    |   99 +-
 drivers/net/intel/ixgbe/ixgbe_ethdev.h    |   71 +-
 drivers/net/intel/ixgbe/ixgbe_fdir.c      |  304 +---
 drivers/net/intel/ixgbe/ixgbe_flow.c      | 1587 +------------------
 drivers/net/intel/ixgbe/ixgbe_flow.h      |    2 +
 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c | 1703 +++++++++++++++++++++
 drivers/net/intel/ixgbe/meson.build       |    1 +
 7 files changed, 1848 insertions(+), 1919 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +43 -56
index f1541a1554..94af935390 100644
@@ -144,8 +144,6 @@ static const char * const ixgbevf_valid_arguments[] = {
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
-static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev);
-static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev);
 static int ixgbe_l2_tn_filter_init(struct rte_eth_dev *eth_dev);
 static int ixgbe_dev_configure(struct rte_eth_dev *dev);
 static int ixgbe_dev_start(struct rte_eth_dev *dev);
@@ -1316,11 +1314,6 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 	memset(filter_info, 0,
 	       sizeof(struct ixgbe_filter_info));
 
-	/* initialize flow director filter list & hash */
-	ret = ixgbe_fdir_filter_init(eth_dev);
-	if (ret)
-		goto err_fdir_filter_init;
-
 	/* initialize l2 tunnel filter list & hash */
 	ret = ixgbe_l2_tn_filter_init(eth_dev);
 	if (ret)
@@ -1345,8 +1338,6 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 
 err_flow_engine_conf_init:
 err_l2_tn_filter_init:
-	ixgbe_fdir_filter_uninit(eth_dev);
-err_fdir_filter_init:
 	ixgbe_disable_intr(hw);
 	rte_intr_disable(intr_handle);
 	rte_intr_callback_unregister(intr_handle,
@@ -1376,32 +1367,24 @@ eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev)
 	return 0;
 }
 
-static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev)
+void
+ixgbe_fdir_state_detach(struct ixgbe_fdir_state *state)
 {
-	struct ixgbe_hw_fdir_info *fdir_info =
-		IXGBE_DEV_PRIVATE_TO_FDIR_INFO(eth_dev->data->dev_private);
-	struct ixgbe_fdir_filter *fdir_filter;
+	if (--state->refcnt > 0)
+		return;
 
-	rte_free(fdir_info->hash_map);
-	rte_hash_free(fdir_info->hash_handle);
-
-	while ((fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list))) {
-		TAILQ_REMOVE(&fdir_info->fdir_list,
-			     fdir_filter,
-			     entries);
-		rte_free(fdir_filter);
-	}
-
-	return 0;
+	state->adapter->fdir_state = NULL;
+	rte_hash_free(state->hash_handle);
+	rte_free(state);
 }
 
-static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev)
+struct ixgbe_fdir_state *
+ixgbe_fdir_state_attach(struct rte_eth_dev_data *dev_data)
 {
-	struct ixgbe_hw_fdir_info *fdir_info =
-		IXGBE_DEV_PRIVATE_TO_FDIR_INFO(eth_dev->data->dev_private);
-	char fdir_hash_name[RTE_HASH_NAMESIZE];
-	struct rte_hash_parameters fdir_hash_params = {
-		.name = fdir_hash_name,
+	struct ixgbe_adapter *adapter = IXGBE_DEV_PRIVATE_TO_ADAPTER(dev_data->dev_private);
+	struct ixgbe_fdir_state *state = adapter->fdir_state;
+	struct rte_hash_parameters hash_params = {
+		.name = "ixgbe_fdir_hash",
 		.entries = IXGBE_MAX_FDIR_FILTER_NUM,
 		.key_len = sizeof(union ixgbe_atr_input),
 		.hash_func = rte_hash_crc,
@@ -1409,31 +1392,36 @@ static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev)
 		.socket_id = rte_socket_id(),
 	};
 
-	TAILQ_INIT(&fdir_info->fdir_list);
-	snprintf(fdir_hash_name, RTE_HASH_NAMESIZE,
-		 "fdir_%s", eth_dev->device->name);
-	fdir_info->hash_handle = rte_hash_create(&fdir_hash_params);
-	if (!fdir_info->hash_handle) {
-		PMD_INIT_LOG(ERR, "Failed to create fdir hash table!");
-		return -EINVAL;
-	}
-	fdir_info->hash_map = rte_zmalloc("ixgbe",
-					  sizeof(struct ixgbe_fdir_filter *) *
-					  IXGBE_MAX_FDIR_FILTER_NUM,
-					  0);
-	if (!fdir_info->hash_map) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir hash map!");
-		rte_hash_free(fdir_info->hash_handle);
-		return -ENOMEM;
+	if (state == NULL) {
+		state = rte_zmalloc("ixgbe_fdir_state", sizeof(*state), 0);
+		if (state == NULL) {
+			PMD_INIT_LOG(ERR, "Failed to allocate fdir state!");
+			return NULL;
+		}
+
+		state->hash_handle = rte_hash_create(&hash_params);
+		if (state->hash_handle == NULL) {
+			PMD_INIT_LOG(ERR, "Failed to create fdir hash table!");
+			goto fail;
+		}
+
+		state->adapter = adapter;
+		state->mode = RTE_FDIR_MODE_NONE;
+
+		/* drop queue is always fixed */
+		IXGBE_DEV_PRIVATE_TO_FDIR_CONF(adapter)->drop_queue = IXGBE_FDIR_DROP_QUEUE;
+
+		adapter->fdir_state = state;
 	}
-	fdir_info->n_flows = 0;
-	fdir_info->mask_added = FALSE;
 
-	/* drop queue is always fixed */
-	IXGBE_DEV_FDIR_CONF(eth_dev)->drop_queue = IXGBE_FDIR_DROP_QUEUE;
+	/* callers are serialized by the flow framework, so no atomics needed */
+	state->refcnt++;
 
-	return 0;
+	return state;
+fail:
+	rte_hash_free(state->hash_handle);
+	rte_free(state);
+	return NULL;
 }
 
 static int ixgbe_l2_tn_filter_init(struct rte_eth_dev *eth_dev)
@@ -2937,6 +2925,9 @@ ixgbe_dev_stop(struct rte_eth_dev *dev)
 	/* reset hierarchy commit */
 	tm_conf->committed = false;
 
+	/* the reset above wiped the flow director setup, kept flows are replayed on start */
+	ixgbe_fdir_hw_invalidate(dev);
+
 	adapter->rss_reta_updated = 0;
 
 	hw->adapter_stopped = true;
@@ -3081,9 +3072,6 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	/* uninitialize PF if max_vfs not zero */
 	ixgbe_pf_host_uninit(dev);
 
-	/* remove all the fdir filters & hash */
-	ixgbe_fdir_filter_uninit(dev);
-
 	/* clear all the filters list */
 	ixgbe_filterlist_flush(dev);
 
@@ -7861,7 +7849,6 @@ ixgbe_rss_filter_restore(struct rte_eth_dev *dev)
 static int
 ixgbe_filter_restore(struct rte_eth_dev *dev)
 {
-	ixgbe_fdir_filter_restore(dev);
 	ixgbe_rss_filter_restore(dev);
 
 	return 0;
drivers/net/intel/ixgbe/ixgbe_ethdev.h +50 -21

110 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_fdir.c +46 -258

354 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.c +3 -1584

1,656 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +2 -0
index 87cf028245..256e0478f5 100644
@@ -20,5 +20,7 @@ extern const struct ci_flow_engine ixgbe_syn_flow_engine;
 extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine;
 extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
 extern const struct ci_flow_engine ixgbe_security_flow_engine;
+extern const struct ci_flow_engine ixgbe_fdir_flow_engine;
+extern const struct ci_flow_engine ixgbe_fdir_tunnel_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_fdir.c new file +1703 -0

718 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 12ba639b70..2487f6a522 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.52.0

Part of the diff is not shown here. View the raw message.

#42 lore raw

PATCH v2 11/19 net/ixgbe: reimplement hash parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for RSS configuration.

RSS flow parser doesn't really parse any "flows", it was completely ignoring the flow patterns and was only looking at actions. It will therefore not specify a pattern graph and will match NULL pattern, empty patterns (START -> END), and ANY patterns (START -> ANY -> END).

RSS was the last engine using "filter list", so that is now removed. The RSS engine filter tracking was also moved completely into the new engine, and all of the "filter list" infrastructure is removed for good.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c    |  30 --
 drivers/net/intel/ixgbe/ixgbe_ethdev.h    |  18 +-
 drivers/net/intel/ixgbe/ixgbe_flow.c      | 415 +---------------------
 drivers/net/intel/ixgbe/ixgbe_flow.h      |   1 +
 drivers/net/intel/ixgbe/ixgbe_flow_hash.c | 212 +++++++++++
 drivers/net/intel/ixgbe/ixgbe_rxtx.c      |  48 +--
 drivers/net/intel/ixgbe/meson.build       |   1 +
 7 files changed, 235 insertions(+), 490 deletions(-)
 create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_hash.c
drivers/net/intel/ixgbe/ixgbe_ethdev.c +0 -30
index 94af935390..0f2188f6a9 100644
@@ -345,7 +345,6 @@ static int ixgbe_dev_udp_tunnel_port_add(struct rte_eth_dev *dev,
 					 struct rte_eth_udp_tunnel *udp_tunnel);
 static int ixgbe_dev_udp_tunnel_port_del(struct rte_eth_dev *dev,
 					 struct rte_eth_udp_tunnel *udp_tunnel);
-static int ixgbe_filter_restore(struct rte_eth_dev *dev);
 static void ixgbe_l2_tunnel_conf(struct rte_eth_dev *dev);
 static int ixgbe_wait_for_link_up(struct ixgbe_hw *hw);
 static int devarg_handle_int(__rte_unused const char *key, const char *value,
@@ -1325,9 +1324,6 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
 	if (ret)
 		goto err_flow_engine_conf_init;
 
-	/* initialize flow filter lists */
-	ixgbe_filterlist_init(eth_dev);
-
 	/* initialize bandwidth configuration info */
 	memset(bw_conf, 0, sizeof(struct ixgbe_bw_conf));
 
@@ -2811,7 +2807,6 @@ ixgbe_dev_start(struct rte_eth_dev *dev)
 	/* resume enabled intr since hw reset */
 	ixgbe_enable_intr(dev);
 	ixgbe_l2_tunnel_conf(dev);
-	ixgbe_filter_restore(dev);
 
 	if (tm_conf->root && !tm_conf->committed)
 		PMD_DRV_LOG(WARNING,
@@ -3072,9 +3067,6 @@ ixgbe_dev_close(struct rte_eth_dev *dev)
 	/* uninitialize PF if max_vfs not zero */
 	ixgbe_pf_host_uninit(dev);
 
-	/* clear all the filters list */
-	ixgbe_filterlist_flush(dev);
-
 	/* Remove all Traffic Manager configuration */
 	ixgbe_tm_conf_uninit(dev);
 
@@ -7832,28 +7824,6 @@ int ixgbe_enable_sec_tx_path_generic(struct ixgbe_hw *hw)
 	return IXGBE_SUCCESS;
 }
 
-/* restore rss filter */
-static inline void
-ixgbe_rss_filter_restore(struct rte_eth_dev *dev)
-{
-	struct ixgbe_adapter *adapter =
-		IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	struct ixgbe_filter_info *filter_info =
-		IXGBE_DEV_PRIVATE_TO_FILTER_INFO(adapter);
-
-	if (filter_info->rss_info.conf.queue_num)
-		ixgbe_config_rss_filter(adapter,
-			&filter_info->rss_info, TRUE);
-}
-
-static int
-ixgbe_filter_restore(struct rte_eth_dev *dev)
-{
-	ixgbe_rss_filter_restore(dev);
-
-	return 0;
-}
-
 static void
 ixgbe_l2_tunnel_conf(struct rte_eth_dev *dev)
 {
drivers/net/intel/ixgbe/ixgbe_ethdev.h +4 -14
index 8c1f421651..cce8363a70 100644
@@ -317,8 +317,6 @@ struct ixgbe_ethertype_table {
  * Structure to store filters' info.
  */
 struct ixgbe_filter_info {
-	/* store the rss filter info */
-	struct ixgbe_rte_flow_rss_conf rss_info;
 	/* shared EtherType (ETQF) slot table */
 	struct ixgbe_ethertype_table ethertype_table;
 	/* 1588 timestamping ETQF slot (valid when timesync_installed) */
@@ -335,10 +333,9 @@ struct ixgbe_l2_tn_info {
 	uint16_t e_tag_ether_type; /* ether type for e-tag */
 };
 
+/* no driver-specific data needed */
 struct rte_flow {
 	struct ci_flow flow;
-	enum rte_filter_type filter_type;
-	void *rule;
 };
 
 struct ixgbe_macsec_setting {
@@ -446,9 +443,6 @@ struct ixgbe_tm_conf {
 	bool committed;
 };
 
-struct ixgbe_filter_ele_base;
-TAILQ_HEAD(ixgbe_filter_ele_list, ixgbe_filter_ele_base);
-
 /*
  * Structure to store private data for each driver instance (for each port).
  */
@@ -472,7 +466,6 @@ struct ixgbe_adapter {
 	struct ixgbe_bypass_info    bps;
 #endif /* RTE_LIBRTE_IXGBE_BYPASS */
 	struct ixgbe_filter_info    filter;
-	struct ixgbe_filter_ele_list flow_list;
 	struct ixgbe_l2_tn_info     l2_tn;
 	struct ixgbe_bw_conf        bw_conf;
 	struct ixgbe_ipsec          ipsec;
@@ -694,8 +687,6 @@ ixgbe_e_tag_filter_add(struct ixgbe_adapter *adapter,
 int
 ixgbe_e_tag_filter_del(struct ixgbe_adapter *adapter,
 		       struct ixgbe_l2_tunnel_conf *l2_tunnel);
-void ixgbe_filterlist_init(struct rte_eth_dev *dev);
-void ixgbe_filterlist_flush(struct rte_eth_dev *dev);
 /*
  * Flow director function prototypes
  */
@@ -767,10 +758,9 @@ int ixgbe_set_queue_rate_limit(struct rte_eth_dev *dev, uint16_t queue_idx,
 			       uint32_t tx_rate);
 int ixgbe_rss_conf_init(struct ixgbe_rte_flow_rss_conf *out,
 			const struct rte_flow_action_rss *in);
-int ixgbe_action_rss_same(const struct rte_flow_action_rss *comp,
-			  const struct rte_flow_action_rss *with);
-int ixgbe_config_rss_filter(struct ixgbe_adapter *adapter,
-		struct ixgbe_rte_flow_rss_conf *conf, bool add);
+void ixgbe_hw_rss_filter_program(struct ixgbe_hw *hw,
+		struct ixgbe_rte_flow_rss_conf *conf);
+void ixgbe_hw_rss_filter_clear(struct ixgbe_hw *hw);
 
 void ixgbe_dev_macsec_register_enable(struct rte_eth_dev *dev,
 		struct ixgbe_macsec_setting *macsec_setting);
drivers/net/intel/ixgbe/ixgbe_flow.c +8 -407

476 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_flow.h +1 -0
index 256e0478f5..959b2c9594 100644
@@ -22,5 +22,6 @@ extern const struct ci_flow_engine ixgbe_ntuple_flow_engine;
 extern const struct ci_flow_engine ixgbe_security_flow_engine;
 extern const struct ci_flow_engine ixgbe_fdir_flow_engine;
 extern const struct ci_flow_engine ixgbe_fdir_tunnel_flow_engine;
+extern const struct ci_flow_engine ixgbe_hash_flow_engine;
 
 #endif /*  _IXGBE_FLOW_H_ */
drivers/net/intel/ixgbe/ixgbe_flow_hash.c new file +212 -0

212 lines not on this page · raw message

drivers/net/intel/ixgbe/ixgbe_rxtx.c +9 -39

75 lines not on this page · raw message

drivers/net/intel/ixgbe/meson.build +1 -0
index 2487f6a522..7e26700835 100644
@@ -32,6 +32,7 @@ sources += files(
         'ixgbe_flow_ntuple.c',
         'ixgbe_flow_security.c',
         'ixgbe_flow_fdir.c',
+        'ixgbe_flow_hash.c',
         'ixgbe_ipsec.c',
         'ixgbe_pf.c',
         'ixgbe_rxtx.c',
Signature · 1 line
2.52.0
#43 lore raw

PATCH v2 12/19 net/ixgbe: advertise flow keep capability

In reply to Anatoly Burakov

The ixgbe driver's rte_flow implementation has always endeavored to keep flows across device restart, however it never advertised this capability. Advertise it explicitly.

---
 drivers/net/intel/ixgbe/ixgbe_ethdev.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/intel/ixgbe/ixgbe_ethdev.c +3 -0
index 0f2188f6a9..b8bcd12b6a 100644
@@ -3968,6 +3968,9 @@ ixgbe_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	dev_info->reta_size = ixgbe_reta_size_get(hw->mac.type);
 	dev_info->flow_type_rss_offloads = IXGBE_RSS_OFFLOAD_ALL;
 
+	/* rte_flow rules are kept across dev_stop/dev_start and replayed on start */
+	dev_info->dev_capa |= RTE_ETH_DEV_CAPA_FLOW_RULE_KEEP;
+
 	dev_info->speed_capa = RTE_ETH_LINK_SPEED_1G | RTE_ETH_LINK_SPEED_10G;
 	if (hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T ||
 			hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T_L)
Signature · 1 line
2.52.0
#44 lore raw

PATCH v2 13/19 net/i40e: add support for common flow parsing

In reply to Anatoly Burakov

Implement support for common flow parsing infrastructure in preparation for migration of flow engines.

Currently, i40e explicitly clears KEEP_FLOW capability flag, meaning that it advertises that all flows are flushed on dev stop. However, in practice i40e actually restores all flows on dev start, so while we will not (yet) advertise the KEEP_FLOW capability flag, we will also not flush the flows on dev stop with the new engines, and instead replay them.

---
 drivers/net/intel/i40e/i40e_ethdev.c | 21 +++++++++++
 drivers/net/intel/i40e/i40e_ethdev.h |  5 +++
 drivers/net/intel/i40e/i40e_flow.c   | 53 +++++++++++++++++++++++++++-
 drivers/net/intel/i40e/i40e_flow.h   | 12 +++++++
 4 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow.h
drivers/net/intel/i40e/i40e_ethdev.c +21 -0
index 86cba68430..bff1fbcb5b 100644
@@ -42,6 +42,9 @@
 #include "i40e_regs.h"
 #include "rte_pmd_i40e.h"
 #include "i40e_hash.h"
+#include "i40e_flow.h"
+
+#include "../common/flow_engine.h"
 
 #define ETH_I40E_FLOATING_VEB_ARG	"enable_floating_veb"
 #define ETH_I40E_FLOATING_VEB_LIST_ARG	"floating_veb_list"
@@ -1838,6 +1841,12 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	if (ret < 0)
 		goto err_init_fdir_filter_list;
 
+	/* initialize flow engine configuration */
+	ret = ci_flow_engine_conf_init(&pf->flow_engine_conf,
+			&i40e_flow_engine_list, dev->data);
+	if (ret < 0)
+		goto err_flow_engine_conf_init;
+
 	/* initialize queue region configuration */
 	i40e_init_queue_region_conf(dev);
 
@@ -1846,6 +1855,12 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 
 	return 0;
 
+err_flow_engine_conf_init:
+	rte_free(pf->fdir.fdir_flow_pool.bitmap);
+	rte_free(pf->fdir.fdir_flow_pool.pool);
+	rte_free(pf->fdir.fdir_filter_array);
+	rte_free(pf->fdir.hash_map);
+	rte_hash_free(pf->fdir.hash_table);
 err_init_fdir_filter_list:
 	rte_hash_free(pf->tunnel.hash_table);
 	rte_free(pf->tunnel.hash_map);
@@ -2613,6 +2628,8 @@ i40e_dev_start(struct rte_eth_dev *dev)
 	/* Set the max frame size to HW*/
 	i40e_aq_set_mac_config(hw, max_frame_size, TRUE, false, 0, NULL);
 
+	ci_flow_replay(&pf->flow_engine_conf);
+
 	return I40E_SUCCESS;
 
 tx_err:
@@ -2715,6 +2732,10 @@ i40e_dev_close(struct rte_eth_dev *dev)
 
 	ret = i40e_dev_stop(dev);
 
+	/* free the flows and reset flow config */
+	ci_flow_cleanup(&pf->flow_engine_conf);
+	ci_flow_engine_conf_reset(&pf->flow_engine_conf);
+
 	i40e_dev_free_queues(dev);
 
 	/* Disable interrupt */
drivers/net/intel/i40e/i40e_ethdev.h +5 -0
index 1e64a2d280..16b67268f7 100644
@@ -21,6 +21,8 @@
 #include "base/i40e_type.h"
 #include "base/virtchnl.h"
 
+#include "../common/flow_engine.h"
+
 #define I40E_AQ_LEN               32
 #define I40E_AQ_BUF_SZ            4096
 /* Number of queues per TC should be one of 1, 2, 4, 8, 16, 32, 64 */
@@ -278,6 +280,7 @@ enum i40e_flxpld_layer_idx {
  * Struct to store flow created.
  */
 struct rte_flow {
+	struct ci_flow base;
 	TAILQ_ENTRY(rte_flow) node;
 	enum rte_filter_type filter_type;
 	void *rule;
@@ -1182,6 +1185,8 @@ struct i40e_pf {
 	/* The floating enable flag for the specific VF */
 	bool floating_veb_list[I40E_MAX_VF];
 	struct i40e_flow_list flow_list;
+	/* flow engine configuration */
+	struct ci_flow_engine_conf flow_engine_conf;
 	bool mpls_replace_flag;  /* 1 - MPLS filter replace is done */
 	bool gtp_replace_flag;   /* 1 - GTP-C/U filter replace is done */
 	bool qinq_replace_flag;  /* QINQ filter replace is done */
drivers/net/intel/i40e/i40e_flow.c +52 -1

107 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h new file +12 -0
new file mode 100644
index 0000000000..c958868661
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef _I40E_FLOW_H_
+#define _I40E_FLOW_H_
+
+#include "../common/flow_engine.h"
+
+extern const struct ci_flow_engine_list i40e_flow_engine_list;
+
+#endif /* _I40E_FLOW_H_ */
Signature · 1 line
2.52.0
#45 lore raw

PATCH v2 14/19 net/i40e: reimplement ethertype parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for Ethertype.

The ethertype filter tracking has been moved completely inside the new engine, and the ethertype code is refactored to enabled that use case by decoupling hardware writes from software tracking.

---
 drivers/net/intel/i40e/i40e_ethdev.c         | 247 +------------
 drivers/net/intel/i40e/i40e_ethdev.h         |  30 +-
 drivers/net/intel/i40e/i40e_flow.c           | 298 +---------------
 drivers/net/intel/i40e/i40e_flow.h           |   4 +
 drivers/net/intel/i40e/i40e_flow_ethertype.c | 348 +++++++++++++++++++
 drivers/net/intel/i40e/meson.build           |   1 +
 6 files changed, 367 insertions(+), 561 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_ethertype.c
drivers/net/intel/i40e/i40e_ethdev.c +2 -245
index bff1fbcb5b..8c009e98ba 100644
@@ -392,12 +392,6 @@ static int i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 static int i40e_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
 
-static int i40e_ethertype_filter_convert(
-	const struct rte_eth_ethertype_filter *input,
-	struct i40e_ethertype_filter *filter);
-static int i40e_sw_ethertype_filter_insert(struct i40e_pf *pf,
-				   struct i40e_ethertype_filter *filter);
-
 static int i40e_tunnel_filter_convert(
 	struct i40e_aqc_cloud_filters_element_bb *cld_filter,
 	struct i40e_tunnel_filter *tunnel_filter);
@@ -405,7 +399,6 @@ static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
 				struct i40e_tunnel_filter *tunnel_filter);
 static int i40e_cloud_filter_qinq_create(struct i40e_pf *pf);
 
-static void i40e_ethertype_filter_restore(struct i40e_pf *pf);
 static void i40e_tunnel_filter_restore(struct i40e_pf *pf);
 static void i40e_filter_restore(struct i40e_pf *pf);
 static void i40e_notify_all_vfs_link_status(struct rte_eth_dev *dev);
@@ -1003,51 +996,6 @@ config_floating_veb(struct rte_eth_dev *dev)
 #define I40E_L2_TAGS_S_TAG_SHIFT 1
 #define I40E_L2_TAGS_S_TAG_MASK I40E_MASK(0x1, I40E_L2_TAGS_S_TAG_SHIFT)
 
-static int
-i40e_init_ethtype_filter_list(struct rte_eth_dev *dev)
-{
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	struct i40e_ethertype_rule *ethertype_rule = &pf->ethertype;
-	char ethertype_hash_name[RTE_HASH_NAMESIZE];
-	int ret;
-
-	struct rte_hash_parameters ethertype_hash_params = {
-		.name = ethertype_hash_name,
-		.entries = I40E_MAX_ETHERTYPE_FILTER_NUM,
-		.key_len = sizeof(struct i40e_ethertype_filter_input),
-		.hash_func = rte_hash_crc,
-		.hash_func_init_val = 0,
-		.socket_id = rte_socket_id(),
-	};
-
-	/* Initialize ethertype filter rule list and hash */
-	TAILQ_INIT(&ethertype_rule->ethertype_list);
-	snprintf(ethertype_hash_name, RTE_HASH_NAMESIZE,
-		 "ethertype_%s", dev->device->name);
-	ethertype_rule->hash_table = rte_hash_create(&ethertype_hash_params);
-	if (!ethertype_rule->hash_table) {
-		PMD_INIT_LOG(ERR, "Failed to create ethertype hash table!");
-		return -EINVAL;
-	}
-	ethertype_rule->hash_map = rte_zmalloc("i40e_ethertype_hash_map",
-				       sizeof(struct i40e_ethertype_filter *) *
-				       I40E_MAX_ETHERTYPE_FILTER_NUM,
-				       0);
-	if (!ethertype_rule->hash_map) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for ethertype hash map!");
-		ret = -ENOMEM;
-		goto err_ethertype_hash_map_alloc;
-	}
-
-	return 0;
-
-err_ethertype_hash_map_alloc:
-	rte_hash_free(ethertype_rule->hash_table);
-
-	return ret;
-}
-
 static int
 i40e_init_tunnel_filter_list(struct rte_eth_dev *dev)
 {
@@ -1831,9 +1779,6 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	/* Initialize the filter invalidation configuration */
 	i40e_init_filter_invalidation(pf);
 
-	ret = i40e_init_ethtype_filter_list(dev);
-	if (ret < 0)
-		goto err_init_ethtype_filter_list;
 	ret = i40e_init_tunnel_filter_list(dev);
 	if (ret < 0)
 		goto err_init_tunnel_filter_list;
@@ -1865,9 +1810,6 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	rte_hash_free(pf->tunnel.hash_table);
 	rte_free(pf->tunnel.hash_map);
 err_init_tunnel_filter_list:
-	rte_hash_free(pf->ethertype.hash_table);
-	rte_free(pf->ethertype.hash_map);
-err_init_ethtype_filter_list:
 	rte_intr_callback_unregister(intr_handle,
 		i40e_dev_interrupt_handler, dev);
 	rte_free(dev->data->mac_addrs);
@@ -1890,24 +1832,6 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	return ret;
 }
 
-static void
-i40e_rm_ethtype_filter_list(struct i40e_pf *pf)
-{
-	struct i40e_ethertype_filter *p_ethertype;
-	struct i40e_ethertype_rule *ethertype_rule;
-
-	ethertype_rule = &pf->ethertype;
-	/* Remove all ethertype filter rules and hash */
-	rte_free(ethertype_rule->hash_map);
-	rte_hash_free(ethertype_rule->hash_table);
-
-	while ((p_ethertype = TAILQ_FIRST(&ethertype_rule->ethertype_list))) {
-		TAILQ_REMOVE(&ethertype_rule->ethertype_list,
-			     p_ethertype, rules);
-		rte_free(p_ethertype);
-	}
-}
-
 static void
 i40e_rm_tunnel_filter_list(struct i40e_pf *pf)
 {
@@ -2805,7 +2729,6 @@ i40e_dev_close(struct rte_eth_dev *dev)
 		i40e_msec_delay(500);
 	} while (retries++ < 5);
 
-	i40e_rm_ethtype_filter_list(pf);
 	i40e_rm_tunnel_filter_list(pf);
 	i40e_rm_fdir_filter_list(pf);
 
@@ -9907,130 +9830,16 @@ i40e_set_hash_inset(struct i40e_hw *hw, uint64_t input_set,
 	return 0;
 }
 
-/* Convert ethertype filter structure */
-static int
-i40e_ethertype_filter_convert(const struct rte_eth_ethertype_filter *input,
-			      struct i40e_ethertype_filter *filter)
-{
-	memcpy(&filter->input.mac_addr, &input->mac_addr,
-		RTE_ETHER_ADDR_LEN);
-	filter->input.ether_type = input->ether_type;
-	filter->flags = input->flags;
-	filter->queue = input->queue;
-
-	return 0;
-}
-
-/* Check if there exists the ethertype filter */
-struct i40e_ethertype_filter *
-i40e_sw_ethertype_filter_lookup(struct i40e_ethertype_rule *ethertype_rule,
-				const struct i40e_ethertype_filter_input *input)
-{
-	int ret;
-
-	ret = rte_hash_lookup(ethertype_rule->hash_table, (const void *)input);
-	if (ret < 0)
-		return NULL;
-
-	return ethertype_rule->hash_map[ret];
-}
-
-/* Add ethertype filter in SW list */
-static int
-i40e_sw_ethertype_filter_insert(struct i40e_pf *pf,
-				struct i40e_ethertype_filter *filter)
-{
-	struct i40e_ethertype_rule *rule = &pf->ethertype;
-	int ret;
-
-	ret = rte_hash_add_key(rule->hash_table, &filter->input);
-	if (ret < 0) {
-		PMD_DRV_LOG(ERR,
-			    "Failed to insert ethertype filter"
-			    " to hash table %d!",
-			    ret);
-		return ret;
-	}
-	rule->hash_map[ret] = filter;
-
-	TAILQ_INSERT_TAIL(&rule->ethertype_list, filter, rules);
-
-	return 0;
-}
-
-/* Delete ethertype filter in SW list */
 int
-i40e_sw_ethertype_filter_del(struct i40e_pf *pf,
-			     struct i40e_ethertype_filter_input *input)
-{
-	struct i40e_ethertype_rule *rule = &pf->ethertype;
-	struct i40e_ethertype_filter *filter;
-	int ret;
-
-	ret = rte_hash_del_key(rule->hash_table, input);
-	if (ret < 0) {
-		PMD_DRV_LOG(ERR,
-			    "Failed to delete ethertype filter"
-			    " to hash table %d!",
-			    ret);
-		return ret;
-	}
-	filter = rule->hash_map[ret];
-	rule->hash_map[ret] = NULL;
-
-	TAILQ_REMOVE(&rule->ethertype_list, filter, rules);
-	rte_free(filter);
-
-	return 0;
-}
-
-/*
- * Configure ethertype filter, which can director packet by filtering
- * with mac address and ether_type or only ether_type
- */
-int
-i40e_ethertype_filter_set(struct i40e_pf *pf,
+i40e_ethertype_filter_program(struct i40e_pf *pf,
 			struct rte_eth_ethertype_filter *filter,
 			bool add)
 {
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	struct i40e_ethertype_rule *ethertype_rule = &pf->ethertype;
-	struct i40e_ethertype_filter *ethertype_filter, *node;
-	struct i40e_ethertype_filter check_filter;
 	struct i40e_control_filter_stats stats;
 	uint16_t flags = 0;
 	int ret;
 
-	if (filter->queue >= pf->dev_data->nb_rx_queues) {
-		PMD_DRV_LOG(ERR, "Invalid queue ID");
-		return -EINVAL;
-	}
-	if (filter->ether_type == RTE_ETHER_TYPE_IPV4 ||
-		filter->ether_type == RTE_ETHER_TYPE_IPV6) {
-		PMD_DRV_LOG(ERR,
-			"unsupported ether_type(0x%04x) in control packet filter.",
-			filter->ether_type);
-		return -EINVAL;
-	}
-	if (filter->ether_type == RTE_ETHER_TYPE_VLAN)
-		PMD_DRV_LOG(WARNING,
-			"filter vlan ether_type in first tag is not supported.");
-
-	/* Check if there is the filter in SW list */
-	memset(&check_filter, 0, sizeof(check_filter));
-	i40e_ethertype_filter_convert(filter, &check_filter);
-	node = i40e_sw_ethertype_filter_lookup(ethertype_rule,
-					       &check_filter.input);
-	if (add && node) {
-		PMD_DRV_LOG(ERR, "Conflict with existing ethertype rules!");
-		return -EINVAL;
-	}
-
-	if (!add && !node) {
-		PMD_DRV_LOG(ERR, "There's no corresponding ethertype filter!");
-		return -EINVAL;
-	}
-
 	if (!(filter->flags & RTE_ETHTYPE_FLAGS_MAC))
 		flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC;
 	if (filter->flags & RTE_ETHTYPE_FLAGS_DROP)
@@ -10051,25 +9860,7 @@ i40e_ethertype_filter_set(struct i40e_pf *pf,
 	if (ret < 0)
 		return -ENOSYS;
 
-	/* Add or delete a filter in SW list */
-	if (add) {
-		ethertype_filter = rte_zmalloc("ethertype_filter",
-				       sizeof(*ethertype_filter), 0);
-		if (ethertype_filter == NULL) {
-			PMD_DRV_LOG(ERR, "Failed to alloc memory.");
-			return -ENOMEM;
-		}
-
-		memcpy(ethertype_filter, &check_filter,
-			   sizeof(check_filter));
-		ret = i40e_sw_ethertype_filter_insert(pf, ethertype_filter);
-		if (ret < 0)
-			rte_free(ethertype_filter);
-	} else {
-		ret = i40e_sw_ethertype_filter_del(pf, &node->input);
-	}
-
-	return ret;
+	return 0;
 }
 
 static int
@@ -11640,39 +11431,6 @@ i40e_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu __rte_unused)
 	return 0;
 }
 
-/* Restore ethertype filter */
-static void
-i40e_ethertype_filter_restore(struct i40e_pf *pf)
-{
-	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	struct i40e_ethertype_filter_list
-		*ethertype_list = &pf->ethertype.ethertype_list;
-	struct i40e_ethertype_filter *f;
-	struct i40e_control_filter_stats stats;
-	uint16_t flags;
-
-	TAILQ_FOREACH(f, ethertype_list, rules) {
-		flags = 0;
-		if (!(f->flags & RTE_ETHTYPE_FLAGS_MAC))
-			flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC;
-		if (f->flags & RTE_ETHTYPE_FLAGS_DROP)
-			flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP;
-		flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TO_QUEUE;
-
-		memset(&stats, 0, sizeof(stats));
-		i40e_aq_add_rem_control_packet_filter(hw,
-					    f->input.mac_addr.addr_bytes,
-					    f->input.ether_type,
-					    flags, pf->main_vsi->seid,
-					    f->queue, 1, &stats, NULL);
-	}
-	PMD_DRV_LOG(INFO, "Ethertype filter:"
-		    " mac_etype_used = %u, etype_used = %u,"
-		    " mac_etype_free = %u, etype_free = %u",
-		    stats.mac_etype_used, stats.etype_used,
-		    stats.mac_etype_free, stats.etype_free);
-}
-
 /* Restore tunnel filter */
 static void
 i40e_tunnel_filter_restore(struct i40e_pf *pf)
@@ -11731,7 +11489,6 @@ i40e_tunnel_filter_restore(struct i40e_pf *pf)
 static void
 i40e_filter_restore(struct i40e_pf *pf)
 {
-	i40e_ethertype_filter_restore(pf);
 	i40e_tunnel_filter_restore(pf);
 	i40e_fdir_filter_restore(pf);
 	(void)i40e_hash_filter_restore(pf);
drivers/net/intel/i40e/i40e_ethdev.h +1 -29
index 16b67268f7..f674f7995d 100644
@@ -805,27 +805,6 @@ struct i40e_fdir_info {
 /* Ethertype filter number HW supports */
 #define I40E_MAX_ETHERTYPE_FILTER_NUM 768
 
-/* Ethertype filter struct */
-struct i40e_ethertype_filter_input {
-	struct rte_ether_addr mac_addr;   /* Mac address to match */
-	uint16_t ether_type;          /* Ether type to match */
-};
-
-struct i40e_ethertype_filter {
-	TAILQ_ENTRY(i40e_ethertype_filter) rules;
-	struct i40e_ethertype_filter_input input;
-	uint16_t flags;              /* Flags from RTE_ETHTYPE_FLAGS_* */
-	uint16_t queue;              /* Queue assigned to when match */
-};
-
-TAILQ_HEAD(i40e_ethertype_filter_list, i40e_ethertype_filter);
-
-struct i40e_ethertype_rule {
-	struct i40e_ethertype_filter_list ethertype_list;
-	struct i40e_ethertype_filter  **hash_map;
-	struct rte_hash *hash_table;
-};
-
 /* queue region info */
 struct i40e_queue_region_info {
 	/* the region id for this configuration */
@@ -1176,7 +1155,6 @@ struct i40e_pf {
 	struct i40e_vmdq_info *vmdq;
 
 	struct i40e_fdir_info fdir; /* flow director info */
-	struct i40e_ethertype_rule ethertype; /* Ethertype filter rule */
 	struct i40e_tunnel_rule tunnel; /* Tunnel filter rule */
 	struct i40e_rss_conf_list rss_config_list; /* RSS rule list */
 	struct i40e_queue_regions queue_region; /* queue region info */
@@ -1325,7 +1303,6 @@ extern const struct rte_flow_ops i40e_flow_ops;
 
 struct i40e_filter_ctx {
 	union {
-		struct rte_eth_ethertype_filter ethertype_filter;
 		struct i40e_fdir_filter_conf fdir_filter;
 		struct i40e_tunnel_filter_conf consistent_tunnel_filter;
 		struct i40e_rte_flow_rss_conf rss_conf;
@@ -1407,11 +1384,6 @@ int i40e_rx_burst_mode_get(struct rte_eth_dev *dev, uint16_t queue_id,
 			   struct rte_eth_burst_mode *mode);
 int i40e_tx_burst_mode_get(struct rte_eth_dev *dev, uint16_t queue_id,
 			   struct rte_eth_burst_mode *mode);
-struct i40e_ethertype_filter *
-i40e_sw_ethertype_filter_lookup(struct i40e_ethertype_rule *ethertype_rule,
-			const struct i40e_ethertype_filter_input *input);
-int i40e_sw_ethertype_filter_del(struct i40e_pf *pf,
-				 struct i40e_ethertype_filter_input *input);
 int i40e_sw_fdir_filter_del(struct i40e_pf *pf,
 			    struct i40e_fdir_input *input);
 struct i40e_tunnel_filter *
@@ -1420,7 +1392,7 @@ i40e_sw_tunnel_filter_lookup(struct i40e_tunnel_rule *tunnel_rule,
 int i40e_sw_tunnel_filter_del(struct i40e_pf *pf,
 			      struct i40e_tunnel_filter_input *input);
 uint64_t i40e_get_default_input_set(uint16_t pctype);
-int i40e_ethertype_filter_set(struct i40e_pf *pf,
+int i40e_ethertype_filter_program(struct i40e_pf *pf,
 			      struct rte_eth_ethertype_filter *filter,
 			      bool add);
 struct rte_flow *
drivers/net/intel/i40e/i40e_flow.c +11 -287

398 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h +4 -0
index c958868661..11d13a76fe 100644
@@ -7,6 +7,10 @@
 
 #include "../common/flow_engine.h"
 
+int i40e_get_outer_vlan(struct i40e_pf *pf, uint16_t *tpid);
+
 extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
+extern const struct ci_flow_engine i40e_flow_engine_ethertype;
+
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_ethertype.c new file +348 -0

348 lines not on this page · raw message

drivers/net/intel/i40e/meson.build +1 -0
index 3229233f50..ddc97f9b3b 100644
@@ -33,6 +33,7 @@ sources += files(
         'i40e_pf.c',
         'i40e_fdir.c',
         'i40e_flow.c',
+        'i40e_flow_ethertype.c',
         'i40e_tm.c',
         'i40e_hash.c',
         'i40e_vf_representor.c',
Signature · 1 line
2.52.0
#46 lore raw

PATCH v2 15/19 net/i40e: refactor FDIR engine infrastructure

In reply to Anatoly Burakov

Currently, there are multiple problems with how i40e flow directory feature is implemented, both in terms of how it works with rte_flow, and how it integrates with the PMD-specific packet template API.

For one, these two subsystems, while using shared infrastructure, do not really interact or cooperate, and are built on top of special cases in FDIR path. More specifically, the packet template code does not store its packet in the hash map, and has a different hashing scheme, yet it still registers itself in FDIR flow list, hash table, and hash map. This list is then used by dev_start to restore FDIR filters that user has inserted into the list. These filters, as written, cannot be reprogrammed that way because the information a filter restore function would need is lost on insert (the packet pointer is not added to the hash map).

Another issue is that while rte_flow FDIR code does lazy FDIR init on first added flow, the packet template API does not, even though it too relies on the same hardware feature, nor does it ever do teardown on last FDIR flow.

Yet another issue is how the "filter restore" code itself is implemented, namely that currently it simply does not work. When doing filter restore, the driver will walk every FDIR filter stored in the TAILQ, and attempt to program it. However, inside the program function, there is a deduplication check (to see if flow being installed is already present in the flow hash table), which fails because the flows we are programming come from the same list that is being checked for deduplication, which makes the entire filter restore a no-op.

The FDIR filter programming code itself also has a number of readability problems as well as being otherwise hard to use - SW bookkeeping, validation, and flow programming is interspersed within the code, and it is difficult to reason about what happens when the code is called from this or that context.

So, this refactor does the following:

- Reorganize FDIR internals to track packet templates and rte_flow FDIR flows separately
- Refactor FDIR init/teardown to always happen on first/last rule, so that whichever API happens to call FDIR first, the state is consistent
- Rework the FDIR code to disentangle FDIR flow rule programming, Flex PIT checks, SW bookkeeping, etc. from each other
- Remove both the rte_flow FDIR TAILQ and the hash map (filter array) structure, because they are redundant (information about the flow is already stored in the rte_flow flow list, and hash_map structure only stored pointers to data we also have in that same list)
- Fix FDIR filter restore to replay all configuration correctly, as well as re-init the FDIR queue enablement tracking
- Rework the internal FDIR global state data structure to make a little more sense by grouping things that belong together into structures

Additionally, there was a delay mechanism at flow director rule program time, as when programming a rule we might not know if it's actually possible to install the rule, because space for the rules may come either from our own pool, or it may come from a pool that is shared with other VSI's. However, it only makes sense to wait on rule create (i.e. when it is programmed into the hardware for the first time), but not when we are replaying or removing these rules. So, adjust the waiting mechanism to only wait on FDIR rule creation.

---
 drivers/net/intel/i40e/i40e_ethdev.c  |  168 +---
 drivers/net/intel/i40e/i40e_ethdev.h  |  126 ++-
 drivers/net/intel/i40e/i40e_fdir.c    | 1134 +++++++++++++++++--------
 drivers/net/intel/i40e/i40e_flow.c    |  131 +--
 drivers/net/intel/i40e/rte_pmd_i40e.c |    4 +-
 5 files changed, 928 insertions(+), 635 deletions(-)
drivers/net/intel/i40e/i40e_ethdev.c +13 -155
index 8c009e98ba..25062fe695 100644
@@ -1041,129 +1041,6 @@ i40e_init_tunnel_filter_list(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static int
-i40e_init_fdir_filter_list(struct rte_eth_dev *dev)
-{
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
-	char fdir_hash_name[RTE_HASH_NAMESIZE];
-	uint32_t alloc = hw->func_caps.fd_filters_guaranteed;
-	uint32_t best = hw->func_caps.fd_filters_best_effort;
-	enum i40e_filter_pctype pctype;
-	struct rte_bitmap *bmp = NULL;
-	uint32_t bmp_size;
-	void *mem = NULL;
-	uint32_t i = 0;
-	int ret;
-
-	struct rte_hash_parameters fdir_hash_params = {
-		.name = fdir_hash_name,
-		.entries = I40E_MAX_FDIR_FILTER_NUM,
-		.key_len = sizeof(struct i40e_fdir_input),
-		.hash_func = rte_hash_crc,
-		.hash_func_init_val = 0,
-		.socket_id = rte_socket_id(),
-	};
-
-	/* Initialize flow director filter rule list and hash */
-	TAILQ_INIT(&fdir_info->fdir_list);
-	snprintf(fdir_hash_name, RTE_HASH_NAMESIZE,
-		 "fdir_%s", dev->device->name);
-	fdir_info->hash_table = rte_hash_create(&fdir_hash_params);
-	if (!fdir_info->hash_table) {
-		PMD_INIT_LOG(ERR, "Failed to create fdir hash table!");
-		return -EINVAL;
-	}
-
-	fdir_info->hash_map = rte_zmalloc("i40e_fdir_hash_map",
-					  sizeof(struct i40e_fdir_filter *) *
-					  I40E_MAX_FDIR_FILTER_NUM,
-					  0);
-	if (!fdir_info->hash_map) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir hash map!");
-		ret = -ENOMEM;
-		goto err_fdir_hash_map_alloc;
-	}
-
-	fdir_info->fdir_filter_array = rte_zmalloc("fdir_filter",
-			sizeof(struct i40e_fdir_filter) *
-			I40E_MAX_FDIR_FILTER_NUM,
-			0);
-
-	if (!fdir_info->fdir_filter_array) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir filter array!");
-		ret = -ENOMEM;
-		goto err_fdir_filter_array_alloc;
-	}
-
-	for (pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
-	     pctype <= I40E_FILTER_PCTYPE_L2_PAYLOAD; pctype++)
-		pf->fdir.flow_count[pctype] = 0;
-
-	fdir_info->fdir_space_size = alloc + best;
-	fdir_info->fdir_actual_cnt = 0;
-	fdir_info->fdir_guarantee_total_space = alloc;
-	fdir_info->fdir_guarantee_free_space =
-		fdir_info->fdir_guarantee_total_space;
-
-	PMD_DRV_LOG(INFO, "FDIR guarantee space: %u, best_effort space %u.", alloc, best);
-
-	fdir_info->fdir_flow_pool.pool =
-			rte_zmalloc("i40e_fdir_entry",
-				sizeof(struct i40e_fdir_entry) *
-				fdir_info->fdir_space_size,
-				0);
-
-	if (!fdir_info->fdir_flow_pool.pool) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for bitmap flow!");
-		ret = -ENOMEM;
-		goto err_fdir_bitmap_flow_alloc;
-	}
-
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		fdir_info->fdir_flow_pool.pool[i].idx = i;
-
-	bmp_size =
-		rte_bitmap_get_memory_footprint(fdir_info->fdir_space_size);
-	mem = rte_zmalloc("fdir_bmap", bmp_size, RTE_CACHE_LINE_SIZE);
-	if (mem == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_mem_alloc;
-	}
-	bmp = rte_bitmap_init(fdir_info->fdir_space_size, mem, bmp_size);
-	if (bmp == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to initialization fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_bmp_alloc;
-	}
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		rte_bitmap_set(bmp, i);
-
-	fdir_info->fdir_flow_pool.bitmap = bmp;
-
-	return 0;
-
-err_fdir_bmp_alloc:
-	rte_free(mem);
-err_fdir_mem_alloc:
-	rte_free(fdir_info->fdir_flow_pool.pool);
-err_fdir_bitmap_flow_alloc:
-	rte_free(fdir_info->fdir_filter_array);
-err_fdir_filter_array_alloc:
-	rte_free(fdir_info->hash_map);
-err_fdir_hash_map_alloc:
-	rte_hash_free(fdir_info->hash_table);
-
-	return ret;
-}
-
 static void
 i40e_init_customized_info(struct i40e_pf *pf)
 {
@@ -1782,9 +1659,9 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	ret = i40e_init_tunnel_filter_list(dev);
 	if (ret < 0)
 		goto err_init_tunnel_filter_list;
-	ret = i40e_init_fdir_filter_list(dev);
+	ret = i40e_fdir_flow_store_init(dev);
 	if (ret < 0)
-		goto err_init_fdir_filter_list;
+		goto err_init_fdir_flow_store;
 
 	/* initialize flow engine configuration */
 	ret = ci_flow_engine_conf_init(&pf->flow_engine_conf,
@@ -1801,12 +1678,8 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	return 0;
 
 err_flow_engine_conf_init:
-	rte_free(pf->fdir.fdir_flow_pool.bitmap);
-	rte_free(pf->fdir.fdir_flow_pool.pool);
-	rte_free(pf->fdir.fdir_filter_array);
-	rte_free(pf->fdir.hash_map);
-	rte_hash_free(pf->fdir.hash_table);
-err_init_fdir_filter_list:
+	i40e_fdir_flow_store_free(&pf->fdir);
+err_init_fdir_flow_store:
 	rte_hash_free(pf->tunnel.hash_table);
 	rte_free(pf->tunnel.hash_map);
 err_init_tunnel_filter_list:
@@ -1849,32 +1722,13 @@ i40e_rm_tunnel_filter_list(struct i40e_pf *pf)
 	}
 }
 
-static void
-i40e_rm_fdir_filter_list(struct i40e_pf *pf)
-{
-	struct i40e_fdir_filter *p_fdir;
-	struct i40e_fdir_info *fdir_info;
-
-	fdir_info = &pf->fdir;
-
-	/* Remove all flow director rules */
-	while ((p_fdir = TAILQ_FIRST(&fdir_info->fdir_list)))
-		TAILQ_REMOVE(&fdir_info->fdir_list, p_fdir, rules);
-}
-
 static void
 i40e_fdir_memory_cleanup(struct i40e_pf *pf)
 {
-	struct i40e_fdir_info *fdir_info;
+	struct i40e_fdir_info *fdir_info = &pf->fdir;
 
-	fdir_info = &pf->fdir;
-
-	/* flow director memory cleanup */
-	rte_free(fdir_info->hash_map);
-	rte_hash_free(fdir_info->hash_table);
-	rte_free(fdir_info->fdir_flow_pool.bitmap);
-	rte_free(fdir_info->fdir_flow_pool.pool);
-	rte_free(fdir_info->fdir_filter_array);
+	i40e_fdir_flow_store_free(fdir_info);
+	i40e_fdir_tmpl_store_free(fdir_info);
 }
 
 void i40e_flex_payload_reg_set_default(struct i40e_hw *hw)
@@ -2730,7 +2584,6 @@ i40e_dev_close(struct rte_eth_dev *dev)
 	} while (retries++ < 5);
 
 	i40e_rm_tunnel_filter_list(pf);
-	i40e_rm_fdir_filter_list(pf);
 
 	/* Remove all flows */
 	while ((p_flow = TAILQ_FIRST(&pf->flow_list))) {
@@ -9774,7 +9627,12 @@ i40e_filter_input_set_init(struct i40e_pf *pf)
 		/* store the default input set */
 		if (!pf->support_multi_driver)
 			pf->hash_input_set[pctype] = input_set;
-		pf->fdir.input_set[pctype] = input_set;
+		{
+			struct i40e_fdir_pctype_state *state =
+				&pf->fdir.flows.pctype[pctype];
+
+			state->input_set = input_set;
+		}
 	}
 }
 
drivers/net/intel/i40e/i40e_ethdev.h +87 -39

183 lines not on this page · raw message

drivers/net/intel/i40e/i40e_fdir.c +783 -351

1,446 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.c +43 -88

205 lines not on this page · raw message

drivers/net/intel/i40e/rte_pmd_i40e.c +2 -2
index 2e7943ef8b..5b9234f8e5 100644
@@ -3044,7 +3044,7 @@ int rte_pmd_i40e_flow_add_del_packet_template(
 			uint8_t add)
 {
 	struct rte_eth_dev *dev = &rte_eth_devices[port];
-	struct i40e_fdir_filter_conf filter_conf;
+	struct i40e_fdir_filter filter_conf;
 
 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
 
@@ -3068,7 +3068,7 @@ int rte_pmd_i40e_flow_add_del_packet_template(
 		(enum i40e_fdir_status)conf->action.report_status;
 	filter_conf.action.flex_off = conf->action.flex_off;
 
-	return i40e_flow_add_del_fdir_filter(dev, &filter_conf, add);
+	return i40e_fdir_tmpl_add_del(dev, &filter_conf, add);
 }
 
 RTE_EXPORT_SYMBOL(rte_pmd_i40e_inset_get)
Signature · 1 line
2.52.0
#47 lore raw

PATCH v2 16/19 net/i40e: reimplement FDIR parser

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for flow director.

As a result of transitioning to more formalized validation, some checks have become more stringent. In particular, some protocols (such as SCTP) were previously accepting non-zero or invalid masks and either ignoring them or misinterpreting them to mean "fully masked". This has now been corrected.

The FDIR engine in i40e has also relied on a custom memory allocation scheme for fdir flows - also migrated to the new infrastructure.

Finally, some of the FDIR infrastructure has been migrated into the new flow engine, so that the engine itself is managing its own allocator and its own flow deduplication scheme.

---
 drivers/net/intel/i40e/i40e_ethdev.c    |   11 +-
 drivers/net/intel/i40e/i40e_ethdev.h    |   64 +-
 drivers/net/intel/i40e/i40e_fdir.c      |  351 +---
 drivers/net/intel/i40e/i40e_flow.c      | 1916 +--------------------
 drivers/net/intel/i40e/i40e_flow.h      |    5 +
 drivers/net/intel/i40e/i40e_flow_fdir.c | 2056 +++++++++++++++++++++++
 drivers/net/intel/i40e/meson.build      |    1 +
 7 files changed, 2126 insertions(+), 2278 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_fdir.c
drivers/net/intel/i40e/i40e_ethdev.c +2 -9
index 25062fe695..572dfff13a 100644
@@ -1659,9 +1659,7 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	ret = i40e_init_tunnel_filter_list(dev);
 	if (ret < 0)
 		goto err_init_tunnel_filter_list;
-	ret = i40e_fdir_flow_store_init(dev);
-	if (ret < 0)
-		goto err_init_fdir_flow_store;
+	i40e_fdir_flow_store_init(dev);
 
 	/* initialize flow engine configuration */
 	ret = ci_flow_engine_conf_init(&pf->flow_engine_conf,
@@ -1678,8 +1676,6 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	return 0;
 
 err_flow_engine_conf_init:
-	i40e_fdir_flow_store_free(&pf->fdir);
-err_init_fdir_flow_store:
 	rte_hash_free(pf->tunnel.hash_table);
 	rte_free(pf->tunnel.hash_map);
 err_init_tunnel_filter_list:
@@ -1727,7 +1723,6 @@ i40e_fdir_memory_cleanup(struct i40e_pf *pf)
 {
 	struct i40e_fdir_info *fdir_info = &pf->fdir;
 
-	i40e_fdir_flow_store_free(fdir_info);
 	i40e_fdir_tmpl_store_free(fdir_info);
 }
 
@@ -2588,9 +2583,7 @@ i40e_dev_close(struct rte_eth_dev *dev)
 	/* Remove all flows */
 	while ((p_flow = TAILQ_FIRST(&pf->flow_list))) {
 		TAILQ_REMOVE(&pf->flow_list, p_flow, node);
-		/* Do not free FDIR flows since they are static allocated */
-		if (p_flow->filter_type != RTE_ETH_FILTER_FDIR)
-			rte_free(p_flow);
+		rte_free(p_flow);
 	}
 
 	/* release the fdir static allocated memory */
drivers/net/intel/i40e/i40e_ethdev.h +22 -42

115 lines not on this page · raw message

drivers/net/intel/i40e/i40e_fdir.c +30 -321

526 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.c +10 -1906

2,047 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h +5 -0
index 11d13a76fe..6823dbef33 100644
@@ -8,9 +8,14 @@
 #include "../common/flow_engine.h"
 
 int i40e_get_outer_vlan(struct i40e_pf *pf, uint16_t *tpid);
+uint8_t
+i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
+		enum rte_flow_item_type item_type,
+		struct i40e_fdir_filter *filter);
 
 extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
 extern const struct ci_flow_engine i40e_flow_engine_ethertype;
+extern const struct ci_flow_engine i40e_flow_engine_fdir;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_fdir.c new file +2056 -0

263 lines not on this page · raw message

drivers/net/intel/i40e/meson.build +1 -0
index ddc97f9b3b..c07257cb80 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.52.0

Part of the diff is not shown here. View the raw message.

#48 lore raw

PATCH v2 17/19 net/i40e: reimplement tunnel parsers

In reply to Anatoly Burakov

Use the new flow graph API and the common parsing framework to implement flow parser for tunnel filters: QinQ, VXLAN, NVGRE, MPLS, GTP, and L4.

As a result of transitioning to more formalized validation, some checks have become more stringent:

- VLAN TCI mask is now required to be fully masked (all-ones); previously the mask was only checked for eth_proto and any non-zero vlan_tci mask value was silently accepted

In addition to using the new graph infrastructure, some of the checks were made more stringent and/or more correct. In particular:

- old code did not check for whether fields other than ports are masked (they are now rejected)
- old code did not check for whether src/ports are fully masked (masks other than full are now rejected)
- old code used spec to decide which port to copy (as a result, it was not possible to match port 0 - this is now allowed)

Tunnel engine now also share a refcounted global state, and track all flows and do deduplication inside the engine.

---
 drivers/net/intel/i40e/i40e_ethdev.c      |  548 +++----
 drivers/net/intel/i40e/i40e_ethdev.h      |   52 +-
 drivers/net/intel/i40e/i40e_flow.c        | 1702 +--------------------
 drivers/net/intel/i40e/i40e_flow.h        |    6 +
 drivers/net/intel/i40e/i40e_flow_tunnel.c | 1590 +++++++++++++++++++
 drivers/net/intel/i40e/meson.build        |    1 +
 6 files changed, 1829 insertions(+), 2070 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_tunnel.c
drivers/net/intel/i40e/i40e_ethdev.c +211 -337
index 572dfff13a..97211b5994 100644
@@ -392,14 +392,8 @@ static int i40e_set_default_mac_addr(struct rte_eth_dev *dev,
 
 static int i40e_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
 
-static int i40e_tunnel_filter_convert(
-	struct i40e_aqc_cloud_filters_element_bb *cld_filter,
-	struct i40e_tunnel_filter *tunnel_filter);
-static int i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
-				struct i40e_tunnel_filter *tunnel_filter);
 static int i40e_cloud_filter_qinq_create(struct i40e_pf *pf);
 
-static void i40e_tunnel_filter_restore(struct i40e_pf *pf);
 static void i40e_filter_restore(struct i40e_pf *pf);
 static void i40e_notify_all_vfs_link_status(struct rte_eth_dev *dev);
 static int i40e_fec_get_capability(struct rte_eth_dev *dev,
@@ -996,49 +990,41 @@ config_floating_veb(struct rte_eth_dev *dev)
 #define I40E_L2_TAGS_S_TAG_SHIFT 1
 #define I40E_L2_TAGS_S_TAG_MASK I40E_MASK(0x1, I40E_L2_TAGS_S_TAG_SHIFT)
 
-static int
-i40e_init_tunnel_filter_list(struct rte_eth_dev *dev)
+struct i40e_tunnel_state *
+i40e_tunnel_state_attach(struct rte_eth_dev_data *dev_data)
 {
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	struct i40e_tunnel_rule *tunnel_rule = &pf->tunnel;
-	char tunnel_hash_name[RTE_HASH_NAMESIZE];
-	int ret;
-
-	struct rte_hash_parameters tunnel_hash_params = {
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev_data->dev_private);
+	struct i40e_tunnel_state *state = &pf->tunnel_state;
+	char tunnel_hash_name[RTE_HASH_NAMESIZE] = {0};
+	struct rte_hash_parameters hash_params = {
 		.name = tunnel_hash_name,
 		.entries = I40E_MAX_TUNNEL_FILTER_NUM,
-		.key_len = sizeof(struct i40e_tunnel_filter_input),
+		.key_len = sizeof(struct i40e_tunnel_filter_match_key),
 		.hash_func = rte_hash_crc,
 		.hash_func_init_val = 0,
 		.socket_id = rte_socket_id(),
 	};
 
-	/* Initialize tunnel filter rule list and hash */
-	TAILQ_INIT(&tunnel_rule->tunnel_list);
-	snprintf(tunnel_hash_name, RTE_HASH_NAMESIZE,
-		 "tunnel_%s", dev->device->name);
-	tunnel_rule->hash_table = rte_hash_create(&tunnel_hash_params);
-	if (!tunnel_rule->hash_table) {
-		PMD_INIT_LOG(ERR, "Failed to create tunnel hash table!");
-		return -EINVAL;
-	}
-	tunnel_rule->hash_map = rte_zmalloc("i40e_tunnel_hash_map",
-				    sizeof(struct i40e_tunnel_filter *) *
-				    I40E_MAX_TUNNEL_FILTER_NUM,
-				    0);
-	if (!tunnel_rule->hash_map) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for tunnel hash map!");
-		ret = -ENOMEM;
-		goto err_tunnel_hash_map_alloc;
+	snprintf(tunnel_hash_name, RTE_HASH_NAMESIZE, "i40e_tunnel_hash_%d", dev_data->port_id);
+
+	if (state->refcnt == 0) {
+		state->hash_table = rte_hash_create(&hash_params);
+		if (state->hash_table == NULL)
+			return NULL;
 	}
 
-	return 0;
+	state->refcnt++;
+	return state;
+}
 
-err_tunnel_hash_map_alloc:
-	rte_hash_free(tunnel_rule->hash_table);
+void
+i40e_tunnel_state_detach(struct i40e_tunnel_state *state)
+{
+	if (--state->refcnt > 0)
+		return;
 
-	return ret;
+	rte_hash_free(state->hash_table);
+	*state = (struct i40e_tunnel_state){0};
 }
 
 static void
@@ -1656,9 +1642,6 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	/* Initialize the filter invalidation configuration */
 	i40e_init_filter_invalidation(pf);
 
-	ret = i40e_init_tunnel_filter_list(dev);
-	if (ret < 0)
-		goto err_init_tunnel_filter_list;
 	i40e_fdir_flow_store_init(dev);
 
 	/* initialize flow engine configuration */
@@ -1676,9 +1659,6 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	return 0;
 
 err_flow_engine_conf_init:
-	rte_hash_free(pf->tunnel.hash_table);
-	rte_free(pf->tunnel.hash_map);
-err_init_tunnel_filter_list:
 	rte_intr_callback_unregister(intr_handle,
 		i40e_dev_interrupt_handler, dev);
 	rte_free(dev->data->mac_addrs);
@@ -1701,23 +1681,6 @@ eth_i40e_dev_init(struct rte_eth_dev *dev, void *init_params __rte_unused)
 	return ret;
 }
 
-static void
-i40e_rm_tunnel_filter_list(struct i40e_pf *pf)
-{
-	struct i40e_tunnel_filter *p_tunnel;
-	struct i40e_tunnel_rule *tunnel_rule;
-
-	tunnel_rule = &pf->tunnel;
-	/* Remove all tunnel director rules and hash */
-	rte_free(tunnel_rule->hash_map);
-	rte_hash_free(tunnel_rule->hash_table);
-
-	while ((p_tunnel = TAILQ_FIRST(&tunnel_rule->tunnel_list))) {
-		TAILQ_REMOVE(&tunnel_rule->tunnel_list, p_tunnel, rules);
-		rte_free(p_tunnel);
-	}
-}
-
 static void
 i40e_fdir_memory_cleanup(struct i40e_pf *pf)
 {
@@ -2578,8 +2541,6 @@ i40e_dev_close(struct rte_eth_dev *dev)
 		i40e_msec_delay(500);
 	} while (retries++ < 5);
 
-	i40e_rm_tunnel_filter_list(pf);
-
 	/* Remove all flows */
 	while ((p_flow = TAILQ_FIRST(&pf->flow_list))) {
 		TAILQ_REMOVE(&pf->flow_list, p_flow, node);
@@ -7747,96 +7708,6 @@ i40e_dev_get_filter_type(uint16_t filter_type, uint16_t *flag)
 	return 0;
 }
 
-/* Convert tunnel filter structure */
-static int
-i40e_tunnel_filter_convert(
-	struct i40e_aqc_cloud_filters_element_bb *cld_filter,
-	struct i40e_tunnel_filter *tunnel_filter)
-{
-	rte_ether_addr_copy((struct rte_ether_addr *)
-			&cld_filter->element.outer_mac,
-		(struct rte_ether_addr *)&tunnel_filter->input.outer_mac);
-	rte_ether_addr_copy((struct rte_ether_addr *)
-			&cld_filter->element.inner_mac,
-		(struct rte_ether_addr *)&tunnel_filter->input.inner_mac);
-	tunnel_filter->input.inner_vlan = cld_filter->element.inner_vlan;
-	if ((rte_le_to_cpu_16(cld_filter->element.flags) &
-	     I40E_AQC_ADD_CLOUD_FLAGS_IPV6) ==
-	    I40E_AQC_ADD_CLOUD_FLAGS_IPV6)
-		tunnel_filter->input.ip_type = I40E_TUNNEL_IPTYPE_IPV6;
-	else
-		tunnel_filter->input.ip_type = I40E_TUNNEL_IPTYPE_IPV4;
-	tunnel_filter->input.flags = cld_filter->element.flags;
-	tunnel_filter->input.tenant_id = cld_filter->element.tenant_id;
-	tunnel_filter->queue = cld_filter->element.queue_number;
-	memcpy(tunnel_filter->input.general_fields,
-		   cld_filter->general_fields,
-		   sizeof(cld_filter->general_fields));
-
-	return 0;
-}
-
-/* Check if there exists the tunnel filter */
-struct i40e_tunnel_filter *
-i40e_sw_tunnel_filter_lookup(struct i40e_tunnel_rule *tunnel_rule,
-			     const struct i40e_tunnel_filter_input *input)
-{
-	int ret;
-
-	ret = rte_hash_lookup(tunnel_rule->hash_table, (const void *)input);
-	if (ret < 0)
-		return NULL;
-
-	return tunnel_rule->hash_map[ret];
-}
-
-/* Add a tunnel filter into the SW list */
-static int
-i40e_sw_tunnel_filter_insert(struct i40e_pf *pf,
-			     struct i40e_tunnel_filter *tunnel_filter)
-{
-	struct i40e_tunnel_rule *rule = &pf->tunnel;
-	int ret;
-
-	ret = rte_hash_add_key(rule->hash_table, &tunnel_filter->input);
-	if (ret < 0) {
-		PMD_DRV_LOG(ERR,
-			    "Failed to insert tunnel filter to hash table %d!",
-			    ret);
-		return ret;
-	}
-	rule->hash_map[ret] = tunnel_filter;
-
-	TAILQ_INSERT_TAIL(&rule->tunnel_list, tunnel_filter, rules);
-
-	return 0;
-}
-
-/* Delete a tunnel filter from the SW list */
-int
-i40e_sw_tunnel_filter_del(struct i40e_pf *pf,
-			  struct i40e_tunnel_filter_input *input)
-{
-	struct i40e_tunnel_rule *rule = &pf->tunnel;
-	struct i40e_tunnel_filter *tunnel_filter;
-	int ret;
-
-	ret = rte_hash_del_key(rule->hash_table, input);
-	if (ret < 0) {
-		PMD_DRV_LOG(ERR,
-			    "Failed to delete tunnel filter to hash table %d!",
-			    ret);
-		return ret;
-	}
-	tunnel_filter = rule->hash_map[ret];
-	rule->hash_map[ret] = NULL;
-
-	TAILQ_REMOVE(&rule->tunnel_list, tunnel_filter, rules);
-	rte_free(tunnel_filter);
-
-	return 0;
-}
-
 #define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_TR_WORD0 0x48
 #define I40E_TR_VXLAN_GRE_KEY_MASK		0x4
 #define I40E_TR_GENEVE_KEY_MASK			0x8
@@ -8275,41 +8146,35 @@ i40e_replace_port_cloud_filter(struct i40e_pf *pf,
 	return status;
 }
 
-int
-i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
-		      struct i40e_tunnel_filter_conf *tunnel_filter,
-		      uint8_t add)
+static int
+i40e_tunnel_filter_convert_conf(struct i40e_pf *pf,
+		struct i40e_tunnel_filter_conf *tunnel_filter,
+		struct i40e_aqc_cloud_filters_element_bb *cld_filter,
+		struct i40e_vsi **vsi, bool *big_buffer)
 {
 	uint16_t ip_type;
 	uint32_t ipv4_addr, ipv4_addr_le;
 	uint8_t i, tun_type = 0;
 	/* internal variable to convert ipv6 byte order */
 	uint32_t convert_ipv6[4];
-	int val, ret = 0;
+	int val;
 	struct i40e_pf_vf *vf = NULL;
-	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	struct i40e_vsi *vsi;
-	struct i40e_aqc_cloud_filters_element_bb cld_filter = {0};
-	struct i40e_tunnel_rule *tunnel_rule = &pf->tunnel;
-	struct i40e_tunnel_filter *node;
-	struct i40e_tunnel_filter check_filter; /* Check if filter exists */
 	uint32_t teid_le;
-	bool big_buffer = 0;
 
 	rte_ether_addr_copy(&tunnel_filter->outer_mac,
-			(struct rte_ether_addr *)&cld_filter.element.outer_mac);
+			(struct rte_ether_addr *)&cld_filter->element.outer_mac);
 	rte_ether_addr_copy(&tunnel_filter->inner_mac,
-			(struct rte_ether_addr *)&cld_filter.element.inner_mac);
+			(struct rte_ether_addr *)&cld_filter->element.inner_mac);
 
-	cld_filter.element.inner_vlan =
+	cld_filter->element.inner_vlan =
 		rte_cpu_to_le_16(tunnel_filter->inner_vlan);
 	if (tunnel_filter->ip_type == I40E_TUNNEL_IPTYPE_IPV4) {
 		ip_type = I40E_AQC_ADD_CLOUD_FLAGS_IPV4;
 		ipv4_addr = rte_be_to_cpu_32(tunnel_filter->ip_addr.ipv4_addr);
 		ipv4_addr_le = rte_cpu_to_le_32(ipv4_addr);
-		memcpy(&cld_filter.element.ipaddr.v4.data,
+		memcpy(&cld_filter->element.ipaddr.v4.data,
 				&ipv4_addr_le,
-				sizeof(cld_filter.element.ipaddr.v4.data));
+				sizeof(cld_filter->element.ipaddr.v4.data));
 	} else {
 		ip_type = I40E_AQC_ADD_CLOUD_FLAGS_IPV6;
 		for (i = 0; i < 4; i++) {
@@ -8317,9 +8182,9 @@ i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
 			rte_cpu_to_le_32(rte_be_to_cpu_32(
 					 tunnel_filter->ip_addr.ipv6_addr[i]));
 		}
-		memcpy(&cld_filter.element.ipaddr.v6.data,
+		memcpy(&cld_filter->element.ipaddr.v6.data,
 			   &convert_ipv6,
-			   sizeof(cld_filter.element.ipaddr.v6.data));
+			   sizeof(cld_filter->element.ipaddr.v6.data));
 	}
 
 	/* check tunneled type */
@@ -8334,137 +8199,96 @@ i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
 		tun_type = I40E_AQC_ADD_CLOUD_TNL_TYPE_IP;
 		break;
 	case I40E_TUNNEL_TYPE_MPLSoUDP:
-		if (!pf->mpls_replace_flag) {
-			i40e_replace_mpls_l1_filter(pf);
-			i40e_replace_mpls_cloud_filter(pf);
-			pf->mpls_replace_flag = 1;
-		}
 		teid_le = rte_cpu_to_le_32(tunnel_filter->tenant_id);
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0] =
 			teid_le >> 4;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
 			(teid_le & 0xF) << 12;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2] =
 			0x40;
-		big_buffer = 1;
+		*big_buffer = 1;
 		tun_type = I40E_AQC_ADD_CLOUD_TNL_TYPE_MPLSOUDP;
 		break;
 	case I40E_TUNNEL_TYPE_MPLSoGRE:
-		if (!pf->mpls_replace_flag) {
-			i40e_replace_mpls_l1_filter(pf);
-			i40e_replace_mpls_cloud_filter(pf);
-			pf->mpls_replace_flag = 1;
-		}
 		teid_le = rte_cpu_to_le_32(tunnel_filter->tenant_id);
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0] =
 			teid_le >> 4;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
 			(teid_le & 0xF) << 12;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2] =
 			0x0;
-		big_buffer = 1;
+		*big_buffer = 1;
 		tun_type = I40E_AQC_ADD_CLOUD_TNL_TYPE_MPLSOGRE;
 		break;
 	case I40E_TUNNEL_TYPE_GTPC:
-		if (!pf->gtp_replace_flag) {
-			i40e_replace_gtp_l1_filter(pf);
-			i40e_replace_gtp_cloud_filter(pf);
-			pf->gtp_replace_flag = 1;
-		}
 		teid_le = rte_cpu_to_le_32(tunnel_filter->tenant_id);
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0] =
 			(teid_le >> 16) & 0xFFFF;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1] =
 			teid_le & 0xFFFF;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2] =
 			0x0;
-		big_buffer = 1;
+		*big_buffer = 1;
 		break;
 	case I40E_TUNNEL_TYPE_GTPU:
-		if (!pf->gtp_replace_flag) {
-			i40e_replace_gtp_l1_filter(pf);
-			i40e_replace_gtp_cloud_filter(pf);
-			pf->gtp_replace_flag = 1;
-		}
 		teid_le = rte_cpu_to_le_32(tunnel_filter->tenant_id);
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0] =
 			(teid_le >> 16) & 0xFFFF;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1] =
 			teid_le & 0xFFFF;
-		cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2] =
+		cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2] =
 			0x0;
-		big_buffer = 1;
+		*big_buffer = 1;
 		break;
 	case I40E_TUNNEL_TYPE_QINQ:
-		if (!pf->qinq_replace_flag) {
-			ret = i40e_cloud_filter_qinq_create(pf);
-			if (ret < 0)
-				PMD_DRV_LOG(DEBUG,
-					    "QinQ tunnel filter already created.");
-			pf->qinq_replace_flag = 1;
-		}
 		/*	Add in the General fields the values of
 		 *	the Outer and Inner VLAN
 		 *	Big Buffer should be set, see changes in
 		 *	i40e_aq_add_cloud_filters
 		 */
-		cld_filter.general_fields[0] = tunnel_filter->inner_vlan;
-		cld_filter.general_fields[1] = tunnel_filter->outer_vlan;
-		big_buffer = 1;
+		cld_filter->general_fields[0] = tunnel_filter->inner_vlan;
+		cld_filter->general_fields[1] = tunnel_filter->outer_vlan;
+		*big_buffer = 1;
 		break;
 	case I40E_CLOUD_TYPE_UDP:
 	case I40E_CLOUD_TYPE_TCP:
 	case I40E_CLOUD_TYPE_SCTP:
 		if (tunnel_filter->l4_port_type == I40E_L4_PORT_TYPE_SRC) {
-			if (!pf->sport_replace_flag) {
-				i40e_replace_port_l1_filter(pf,
-						tunnel_filter->l4_port_type);
-				i40e_replace_port_cloud_filter(pf,
-						tunnel_filter->l4_port_type);
-				pf->sport_replace_flag = 1;
-			}
 			teid_le = rte_cpu_to_le_32(tunnel_filter->tenant_id);
-			cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0] =
+			cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0] =
 				I40E_DIRECTION_INGRESS_KEY;
 
 			if (tunnel_filter->tunnel_type == I40E_CLOUD_TYPE_UDP)
-				cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
+				cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
 					I40E_TR_L4_TYPE_UDP;
 			else if (tunnel_filter->tunnel_type == I40E_CLOUD_TYPE_TCP)
-				cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
+				cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
 					I40E_TR_L4_TYPE_TCP;
 			else
-				cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
+				cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1] =
 					I40E_TR_L4_TYPE_SCTP;
 
-			cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2] =
+			cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2] =
 				(teid_le >> 16) & 0xFFFF;
-			big_buffer = 1;
+			*big_buffer = 1;
 		} else {
-			if (!pf->dport_replace_flag) {
-				i40e_replace_port_l1_filter(pf,
-						tunnel_filter->l4_port_type);
-				i40e_replace_port_cloud_filter(pf,
-						tunnel_filter->l4_port_type);
-				pf->dport_replace_flag = 1;
-			}
 			teid_le = rte_cpu_to_le_32(tunnel_filter->tenant_id);
-			cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0] =
+			cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0] =
 				I40E_DIRECTION_INGRESS_KEY;
 
 			if (tunnel_filter->tunnel_type == I40E_CLOUD_TYPE_UDP)
-				cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1] =
+				cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1] =
 					I40E_TR_L4_TYPE_UDP;
 			else if (tunnel_filter->tunnel_type == I40E_CLOUD_TYPE_TCP)
-				cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1] =
+				cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1] =
 					I40E_TR_L4_TYPE_TCP;
 			else
-				cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1] =
+				cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1] =
 					I40E_TR_L4_TYPE_SCTP;
 
-			cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2] =
+			cld_filter->general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2] =
 				(teid_le >> 16) & 0xFFFF;
-			big_buffer = 1;
+			*big_buffer = 1;
 		}
 
 		break;
@@ -8475,74 +8299,191 @@ i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
 	}
 
 	if (tunnel_filter->tunnel_type == I40E_TUNNEL_TYPE_MPLSoUDP)
-		cld_filter.element.flags =
+		cld_filter->element.flags =
 			I40E_AQC_ADD_CLOUD_FILTER_0X11;
 	else if (tunnel_filter->tunnel_type == I40E_TUNNEL_TYPE_MPLSoGRE)
-		cld_filter.element.flags =
+		cld_filter->element.flags =
 			I40E_AQC_ADD_CLOUD_FILTER_0X12;
 	else if (tunnel_filter->tunnel_type == I40E_TUNNEL_TYPE_GTPC)
-		cld_filter.element.flags =
+		cld_filter->element.flags =
 			I40E_AQC_ADD_CLOUD_FILTER_0X11;
 	else if (tunnel_filter->tunnel_type == I40E_TUNNEL_TYPE_GTPU)
-		cld_filter.element.flags =
+		cld_filter->element.flags =
 			I40E_AQC_ADD_CLOUD_FILTER_0X12;
 	else if (tunnel_filter->tunnel_type == I40E_TUNNEL_TYPE_QINQ)
-		cld_filter.element.flags |=
+		cld_filter->element.flags |=
 			I40E_AQC_ADD_CLOUD_FILTER_0X10;
 	else if (tunnel_filter->tunnel_type == I40E_CLOUD_TYPE_UDP ||
 		 tunnel_filter->tunnel_type == I40E_CLOUD_TYPE_TCP ||
 		 tunnel_filter->tunnel_type == I40E_CLOUD_TYPE_SCTP) {
 		if (tunnel_filter->l4_port_type == I40E_L4_PORT_TYPE_SRC)
-			cld_filter.element.flags |=
+			cld_filter->element.flags |=
 				I40E_AQC_ADD_CLOUD_FILTER_0X11;
 		else
-			cld_filter.element.flags |=
+			cld_filter->element.flags |=
 				I40E_AQC_ADD_CLOUD_FILTER_0X10;
 	} else {
 		val = i40e_dev_get_filter_type(tunnel_filter->filter_type,
-						&cld_filter.element.flags);
+						&cld_filter->element.flags);
 		if (val < 0) {
 			return -EINVAL;
 		}
 	}
 
-	cld_filter.element.flags |=
+	cld_filter->element.flags |=
 			rte_cpu_to_le_16(I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE | ip_type |
 				(tun_type << I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT));
-	cld_filter.element.tenant_id = rte_cpu_to_le_32(tunnel_filter->tenant_id);
-	cld_filter.element.queue_number =
+	cld_filter->element.tenant_id = rte_cpu_to_le_32(tunnel_filter->tenant_id);
+	cld_filter->element.queue_number =
 		rte_cpu_to_le_16(tunnel_filter->queue_id);
 
 	if (!tunnel_filter->is_to_vf)
-		vsi = pf->main_vsi;
+		*vsi = pf->main_vsi;
 	else {
 		if (tunnel_filter->vf_id >= pf->vf_num) {
 			PMD_DRV_LOG(ERR, "Invalid argument.");
 			return -EINVAL;
 		}
 		vf = &pf->vfs[tunnel_filter->vf_id];
-		vsi = vf->vsi;
+		*vsi = vf->vsi;
 	}
 
-	/* Check if there is the filter in SW list */
-	memset(&check_filter, 0, sizeof(check_filter));
-	i40e_tunnel_filter_convert(&cld_filter, &check_filter);
-	check_filter.is_to_vf = tunnel_filter->is_to_vf;
-	check_filter.vf_id = tunnel_filter->vf_id;
-	node = i40e_sw_tunnel_filter_lookup(tunnel_rule, &check_filter.input);
-	if (add && node) {
-		PMD_DRV_LOG(ERR, "Conflict with existing tunnel rules!");
-		return -EINVAL;
-	}
+	return 0;
+}
+
+static int
+i40e_tunnel_filter_prepare_hw(struct i40e_pf *pf,
+		struct i40e_tunnel_filter_conf *tunnel_filter)
+{
+	int ret;
 
-	if (!add && !node) {
-		PMD_DRV_LOG(ERR, "There's no corresponding tunnel filter!");
-		return -EINVAL;
+	switch (tunnel_filter->tunnel_type) {
+	case I40E_TUNNEL_TYPE_MPLSoUDP:
+	case I40E_TUNNEL_TYPE_MPLSoGRE:
+		if (!pf->mpls_replace_flag) {
+			ret = i40e_replace_mpls_l1_filter(pf);
+			if (ret < 0)
+				return ret;
+			ret = i40e_replace_mpls_cloud_filter(pf);
+			if (ret < 0)
+				return ret;
+			pf->mpls_replace_flag = 1;
+		}
+		break;
+	case I40E_TUNNEL_TYPE_GTPC:
+	case I40E_TUNNEL_TYPE_GTPU:
+		if (!pf->gtp_replace_flag) {
+			ret = i40e_replace_gtp_l1_filter(pf);
+			if (ret < 0)
+				return ret;
+			ret = i40e_replace_gtp_cloud_filter(pf);
+			if (ret < 0)
+				return ret;
+			pf->gtp_replace_flag = 1;
+		}
+		break;
+	case I40E_TUNNEL_TYPE_QINQ:
+		if (!pf->qinq_replace_flag) {
+			ret = i40e_cloud_filter_qinq_create(pf);
+			if (ret < 0)
+				PMD_DRV_LOG(DEBUG,
+					    "QinQ tunnel filter already created.");
+			pf->qinq_replace_flag = 1;
+		}
+		break;
+	case I40E_CLOUD_TYPE_UDP:
+	case I40E_CLOUD_TYPE_TCP:
+	case I40E_CLOUD_TYPE_SCTP:
+		if (tunnel_filter->l4_port_type == I40E_L4_PORT_TYPE_SRC) {
+			if (!pf->sport_replace_flag) {
+				ret = i40e_replace_port_l1_filter(pf,
+						tunnel_filter->l4_port_type);
+				if (ret < 0)
+					return ret;
+				ret = i40e_replace_port_cloud_filter(pf,
+						tunnel_filter->l4_port_type);
+				if (ret < 0)
+					return ret;
+				pf->sport_replace_flag = 1;
+			}
+		} else if (!pf->dport_replace_flag) {
+			ret = i40e_replace_port_l1_filter(pf,
+					tunnel_filter->l4_port_type);
+			if (ret < 0)
+				return ret;
+			ret = i40e_replace_port_cloud_filter(pf,
+					tunnel_filter->l4_port_type);
+			if (ret < 0)
+				return ret;
+			pf->dport_replace_flag = 1;
+		}
+		break;
+	default:
+		break;
 	}
 
+	return 0;
+}
+
+int
+i40e_tunnel_filter_match_key_get(struct i40e_pf *pf,
+		struct i40e_tunnel_filter_conf *tunnel_filter,
+		struct i40e_tunnel_filter_match_key *input)
+{
+	struct i40e_aqc_cloud_filters_element_bb cld_filter = {0};
+	struct i40e_vsi *vsi;
+	bool big_buffer = 0;
+	int ret;
+
+	ret = i40e_tunnel_filter_convert_conf(pf, tunnel_filter, &cld_filter,
+			&vsi, &big_buffer);
+	if (ret != 0)
+		return ret;
+
+	rte_ether_addr_copy((struct rte_ether_addr *)
+			&cld_filter.element.outer_mac,
+		(struct rte_ether_addr *)&input->outer_mac);
+	rte_ether_addr_copy((struct rte_ether_addr *)
+			&cld_filter.element.inner_mac,
+		(struct rte_ether_addr *)&input->inner_mac);
+	input->inner_vlan = cld_filter.element.inner_vlan;
+	if ((rte_le_to_cpu_16(cld_filter.element.flags) &
+	     I40E_AQC_ADD_CLOUD_FLAGS_IPV6) ==
+	    I40E_AQC_ADD_CLOUD_FLAGS_IPV6)
+		input->ip_type = I40E_TUNNEL_IPTYPE_IPV6;
+	else
+		input->ip_type = I40E_TUNNEL_IPTYPE_IPV4;
+	input->flags = cld_filter.element.flags;
+	input->tenant_id = cld_filter.element.tenant_id;
+	memcpy(input->general_fields, cld_filter.general_fields,
+			sizeof(cld_filter.general_fields));
+
+	return 0;
+}
+
+int
+i40e_tunnel_filter_program(struct i40e_pf *pf,
+		struct i40e_tunnel_filter_conf *tunnel_filter,
+		uint8_t add)
+{
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	struct i40e_aqc_cloud_filters_element_bb cld_filter = {0};
+	struct i40e_vsi *vsi;
+	bool big_buffer = 0;
+	int ret;
+
+	ret = i40e_tunnel_filter_convert_conf(pf, tunnel_filter, &cld_filter,
+			&vsi, &big_buffer);
+	if (ret != 0)
+		return ret;
+
 	if (add) {
-		struct i40e_tunnel_filter *tunnel;
+		ret = i40e_tunnel_filter_prepare_hw(pf, tunnel_filter);
+		if (ret < 0)
+			return ret;
+	}
 
+	if (add) {
 		if (big_buffer)
 			ret = i40e_aq_add_cloud_filters_bb(hw,
 						   vsi->seid, &cld_filter, 1);
@@ -8553,16 +8494,6 @@ i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
 			PMD_DRV_LOG(ERR, "Failed to add a tunnel filter.");
 			return -ENOTSUP;
 		}
-		tunnel = rte_zmalloc("tunnel_filter", sizeof(*tunnel), 0);
-		if (tunnel == NULL) {
-			PMD_DRV_LOG(ERR, "Failed to alloc memory.");
-			return -ENOMEM;
-		}
-
-		memcpy(tunnel, &check_filter, sizeof(check_filter));
-		ret = i40e_sw_tunnel_filter_insert(pf, tunnel);
-		if (ret < 0)
-			rte_free(tunnel);
 	} else {
 		if (big_buffer)
 			ret = i40e_aq_rem_cloud_filters_bb(
@@ -8574,7 +8505,6 @@ i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
 			PMD_DRV_LOG(ERR, "Failed to delete a tunnel filter.");
 			return -ENOTSUP;
 		}
-		ret = i40e_sw_tunnel_filter_del(pf, &node->input);
 	}
 
 	return ret;
@@ -11282,65 +11212,9 @@ i40e_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu __rte_unused)
 	return 0;
 }
 
-/* Restore tunnel filter */
-static void
-i40e_tunnel_filter_restore(struct i40e_pf *pf)
-{
-	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
-	struct i40e_vsi *vsi;
-	struct i40e_pf_vf *vf;
-	struct i40e_tunnel_filter_list
-		*tunnel_list = &pf->tunnel.tunnel_list;
-	struct i40e_tunnel_filter *f;
-	struct i40e_aqc_cloud_filters_element_bb cld_filter;
-	bool big_buffer = 0;
-
-	TAILQ_FOREACH(f, tunnel_list, rules) {
-		if (!f->is_to_vf)
-			vsi = pf->main_vsi;
-		else {
-			vf = &pf->vfs[f->vf_id];
-			vsi = vf->vsi;
-		}
-		memset(&cld_filter, 0, sizeof(cld_filter));
-		rte_ether_addr_copy((struct rte_ether_addr *)
-				&f->input.outer_mac,
-			(struct rte_ether_addr *)&cld_filter.element.outer_mac);
-		rte_ether_addr_copy((struct rte_ether_addr *)
-				&f->input.inner_mac,
-			(struct rte_ether_addr *)&cld_filter.element.inner_mac);
-		cld_filter.element.inner_vlan = f->input.inner_vlan;
-		cld_filter.element.flags = f->input.flags;
-		cld_filter.element.tenant_id = f->input.tenant_id;
-		cld_filter.element.queue_number = f->queue;
-		memcpy(cld_filter.general_fields,
-			   f->input.general_fields,
-			   sizeof(f->input.general_fields));
-
-		if (((f->input.flags &
-		     I40E_AQC_ADD_CLOUD_FILTER_0X11) ==
-		     I40E_AQC_ADD_CLOUD_FILTER_0X11) ||
-		    ((f->input.flags &
-		     I40E_AQC_ADD_CLOUD_FILTER_0X12) ==
-		     I40E_AQC_ADD_CLOUD_FILTER_0X12) ||
-		    ((f->input.flags &
-		     I40E_AQC_ADD_CLOUD_FILTER_0X10) ==
-		     I40E_AQC_ADD_CLOUD_FILTER_0X10))
-			big_buffer = 1;
-
-		if (big_buffer)
-			i40e_aq_add_cloud_filters_bb(hw,
-					vsi->seid, &cld_filter, 1);
-		else
-			i40e_aq_add_cloud_filters(hw, vsi->seid,
-						  &cld_filter.element, 1);
-	}
-}
-
 static void
 i40e_filter_restore(struct i40e_pf *pf)
 {
-	i40e_tunnel_filter_restore(pf);
 	i40e_fdir_filter_restore(pf);
 	(void)i40e_hash_filter_restore(pf);
 }
drivers/net/intel/i40e/i40e_ethdev.h +14 -38

92 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.c +7 -1695

1,782 lines not on this page · raw message

drivers/net/intel/i40e/i40e_flow.h +6 -0
index 6823dbef33..28e342210c 100644
@@ -17,5 +17,11 @@ extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
 extern const struct ci_flow_engine i40e_flow_engine_ethertype;
 extern const struct ci_flow_engine i40e_flow_engine_fdir;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_qinq;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_vxlan;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_nvgre;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_mpls;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_gtp;
+extern const struct ci_flow_engine i40e_flow_engine_tunnel_l4;
 
 #endif /* _I40E_FLOW_H_ */
drivers/net/intel/i40e/i40e_flow_tunnel.c new file +1590 -0

347 lines not on this page · raw message

drivers/net/intel/i40e/meson.build +1 -0
index c07257cb80..0db60c1e99 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.52.0

Part of the diff is not shown here. View the raw message.