From: Bing Zhao <bingz@nvidia.com>
To: <viacheslavo@nvidia.com>, <dev@dpdk.org>, <rasland@nvidia.com>,
<stephen@networkplumber.org>
Cc: <orika@nvidia.com>, <dsosnowski@nvidia.com>,
<suanmingm@nvidia.com>, <matan@nvidia.com>, <thomas@monjalon.net>
Subject: [PATCH v8] ethdev: support inline calculating masked item value
Date: Fri, 16 Oct 2026 08:26:45 +0300 [thread overview]
Message-ID: <20261016052645.288851-1-bingz@nvidia.com> (raw)
In-Reply-To: <20261013134950.310252-1-bingz@nvidia.com>
Drivers implementing synchronous flow rules through the asynchronous
template API need to apply item masks before creating a rule. The spec
and last pointers are const, so this requires writable copies and
per-item knowledge of their sizes.
Add RTE_FLOW_CONV_OP_PATTERN_MASKED to rte_flow_conv(). It copies the
pattern like RTE_FLOW_CONV_OP_PATTERN and ANDs the inline fields of each
copied spec and last with an explicit item mask. A NULL mask leaves the
values unchanged; default masks are not substituted.
Preserve GENEVE_OPT.option_len and TAG.index because they describe the
pointed-to data length and select the tag register, respectively.
RAW pattern and GENEVE_OPT data are copied without applying their
payload masks. Limit this operation to inline fields, leaving
variable-length payload masking to the caller. FLEX, COMPARE and PMD
private items are not masked.
Signed-off-by: Bing Zhao <bingz@nvidia.com>
Reviewed-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
v2: add review comments from Dariusz Sosnowski
v3:
- add test code
- fix the issue found by AI
v4: reabse on top of the main
v5: handle some items separately and add test for them
v6: resend the correct version instead of v5 with AI defects resolved
v7: address the review comments from AI
v8:
- preserve GENEVE_OPT option_len and TAG index
- test spec and last preservation, TAG data masking and unchanged masks
- document the masking exceptions and explicit-mask behavior
- clarify the inline-only scope without insertion-rate claims
- rebase on the latest main, retaining the default-mask lookup operation
---
app/test/test_ethdev_api.c | 389 +++++++++++++++++++++++++
doc/guides/rel_notes/release_26_11.rst | 9 +
lib/ethdev/rte_flow.c | 80 ++++-
lib/ethdev/rte_flow.h | 17 ++
4 files changed, 489 insertions(+), 6 deletions(-)
diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c
index 76afd0345c..69cb38a0a1 100644
--- a/app/test/test_ethdev_api.c
+++ b/app/test/test_ethdev_api.c
@@ -2,8 +2,11 @@
* Copyright (C) 2023, Advanced Micro Devices, Inc.
*/
+#include <string.h>
+
#include <rte_log.h>
#include <rte_ethdev.h>
+#include <rte_flow.h>
#include <rte_test.h>
#include "test.h"
@@ -15,6 +18,374 @@
#define NUM_MBUF 1024
#define MBUF_CACHE_SIZE 256
+static int32_t
+ethdev_api_flow_conv_pattern_masked(void)
+{
+ const struct rte_flow_item_eth spec = {
+ .hdr.dst_addr.addr_bytes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 },
+ .hdr.src_addr.addr_bytes = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ .hdr.ether_type = RTE_BE16(0x1234),
+ };
+ const struct rte_flow_item_eth last = {
+ .hdr.dst_addr.addr_bytes = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 },
+ .hdr.src_addr.addr_bytes = { 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+ .hdr.ether_type = RTE_BE16(0x5678),
+ };
+ const struct rte_flow_item_eth mask = {
+ .hdr.dst_addr.addr_bytes = { 0xff, 0xff, 0x00, 0x00, 0xff, 0xff },
+ .hdr.src_addr.addr_bytes = { 0xff, 0x00, 0xff, 0x00, 0xff, 0x00 },
+ .hdr.ether_type = RTE_BE16(0xffff),
+ };
+ const uint8_t raw_pattern[] = { 0xaa, 0xbb, 0xcc, 0xdd };
+ const uint8_t raw_pattern_mask[] = { 0x0f, 0xf0, 0x00, 0xff };
+ const struct rte_flow_item_raw raw_spec = {
+ .relative = 1,
+ .search = 1,
+ .offset = 8,
+ .limit = 4,
+ .length = sizeof(raw_pattern),
+ .pattern = raw_pattern,
+ };
+ const struct rte_flow_item_raw raw_mask = {
+ .relative = 1,
+ .search = 0,
+ .reserved = 0x3fffffff,
+ .offset = 0xffffffff,
+ .limit = 0xffff,
+ .length = 0xffff,
+ .pattern = raw_pattern_mask,
+ };
+ const struct rte_flow_item pattern[] = {
+ {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .spec = &spec,
+ .last = &last,
+ .mask = &mask,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_RAW,
+ .spec = &raw_spec,
+ .mask = &raw_mask,
+ },
+ { .type = RTE_FLOW_ITEM_TYPE_END },
+ };
+ union {
+ struct rte_flow_item item;
+ struct rte_flow_item_eth eth;
+ struct rte_flow_item_raw raw;
+ double align;
+ uint8_t buf[256];
+ } dst;
+ const struct rte_flow_item *item;
+ const struct rte_flow_item_eth *conv_spec;
+ const struct rte_flow_item_eth *conv_last;
+ const struct rte_flow_item_raw *conv_raw_spec;
+ int ret;
+
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, NULL, 0, pattern, NULL);
+ TEST_ASSERT(ret > 0, "Masked pattern conversion size query failed");
+ TEST_ASSERT((size_t)ret <= sizeof(dst.buf),
+ "Masked pattern conversion needs too much storage");
+
+ memset(&dst, 0, sizeof(dst));
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, dst.buf,
+ sizeof(dst.buf), pattern, NULL);
+ TEST_ASSERT(ret > 0, "Masked pattern conversion failed");
+
+ item = (const struct rte_flow_item *)dst.buf;
+ conv_spec = item[0].spec;
+ conv_last = item[0].last;
+ TEST_ASSERT_NOT_NULL(conv_spec, "Converted spec must be set");
+ TEST_ASSERT_NOT_NULL(conv_last, "Converted last must be set");
+
+ TEST_ASSERT_EQUAL(conv_spec->hdr.dst_addr.addr_bytes[0], 0x01,
+ "Masked spec dst byte 0 mismatch");
+ TEST_ASSERT_EQUAL(conv_spec->hdr.dst_addr.addr_bytes[2], 0x00,
+ "Masked spec dst byte 2 mismatch");
+ TEST_ASSERT_EQUAL(conv_spec->hdr.src_addr.addr_bytes[1], 0x00,
+ "Masked spec src byte 1 mismatch");
+ TEST_ASSERT_EQUAL(conv_spec->hdr.ether_type, RTE_BE16(0x1234),
+ "Masked spec ether type mismatch");
+ TEST_ASSERT_EQUAL(conv_last->hdr.dst_addr.addr_bytes[0], 0x11,
+ "Masked last dst byte 0 mismatch");
+ TEST_ASSERT_EQUAL(conv_last->hdr.dst_addr.addr_bytes[2], 0x00,
+ "Masked last dst byte 2 mismatch");
+ TEST_ASSERT_EQUAL(conv_last->hdr.src_addr.addr_bytes[1], 0x00,
+ "Masked last src byte 1 mismatch");
+ TEST_ASSERT_EQUAL(conv_last->hdr.ether_type, RTE_BE16(0x5678),
+ "Masked last ether type mismatch");
+
+ conv_raw_spec = item[1].spec;
+ TEST_ASSERT_NOT_NULL(conv_raw_spec, "Converted RAW spec must be set");
+ TEST_ASSERT_NOT_NULL(conv_raw_spec->pattern,
+ "Converted RAW pattern pointer must be set");
+ TEST_ASSERT(conv_raw_spec->pattern != raw_pattern,
+ "Converted RAW pattern must be deep-copied");
+ TEST_ASSERT_EQUAL(conv_raw_spec->relative, 1,
+ "Masked RAW relative mismatch");
+ TEST_ASSERT_EQUAL(conv_raw_spec->search, 0,
+ "Masked RAW search mismatch");
+ TEST_ASSERT_EQUAL(conv_raw_spec->offset, raw_spec.offset,
+ "Masked RAW offset mismatch");
+ TEST_ASSERT_EQUAL(conv_raw_spec->limit, raw_spec.limit,
+ "Masked RAW limit mismatch");
+ TEST_ASSERT_EQUAL(conv_raw_spec->length, raw_spec.length,
+ "Masked RAW length mismatch");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL(conv_raw_spec->pattern, raw_pattern,
+ sizeof(raw_pattern),
+ "RAW pattern bytes must not be masked");
+
+ /*
+ * NULL mask keeps spec and last unchanged: the IPv4 default mask
+ * (addresses only) is not substituted. An explicit mask is applied
+ * to both spec and last. Private items, GENEVE_OPT length and data,
+ * TAG index and COMPARE items are not masked.
+ */
+ {
+ const struct rte_flow_item_ipv4 ipv4_open = {
+ .hdr = {
+ .type_of_service = 0xab,
+ .time_to_live = 0x40,
+ .src_addr = RTE_BE32(0x0a010203),
+ .dst_addr = RTE_BE32(0xc0a80001),
+ },
+ };
+ const struct rte_flow_item_ipv4 ipv4_spec = {
+ .hdr = {
+ .type_of_service = 0xab,
+ .time_to_live = 0x40,
+ .src_addr = RTE_BE32(0x0a000001),
+ .dst_addr = RTE_BE32(0x0a000002),
+ },
+ };
+ const struct rte_flow_item_ipv4 ipv4_last = {
+ .hdr = {
+ .type_of_service = 0xcd,
+ .time_to_live = 0x20,
+ .src_addr = RTE_BE32(0x0a0000ff),
+ .dst_addr = RTE_BE32(0x0a00ffff),
+ },
+ };
+ const struct rte_flow_item_ipv4 ipv4_mask = {
+ .hdr = {
+ .type_of_service = 0x0f,
+ .src_addr = RTE_BE32(0xffffffff),
+ .dst_addr = RTE_BE32(0x0000ffff),
+ },
+ };
+ const struct {
+ uint64_t value;
+ uint32_t tail;
+ } priv_spec = {
+ .value = 0x1122334455667788ULL,
+ .tail = 0xaabbccdd,
+ };
+ const struct {
+ uint64_t value;
+ uint32_t tail;
+ } priv_mask = {
+ .value = 0,
+ .tail = 0,
+ };
+ uint32_t opt_data[2] = { 0xffffffff, 0x11111111 };
+ uint32_t opt_data_mask[2] = { 0, 0 };
+ const struct rte_flow_item_geneve_opt opt_spec = {
+ .option_class = RTE_BE16(0x1234),
+ .option_type = 0xaa,
+ .option_len = 2,
+ .data = opt_data,
+ };
+ const struct rte_flow_item_geneve_opt opt_mask = {
+ .option_type = 0xff,
+ .data = opt_data_mask,
+ };
+ uint32_t cmp_value = 0x12345678;
+ const struct rte_flow_item_compare cmp_spec = {
+ .operation = RTE_FLOW_ITEM_COMPARE_GT,
+ .a = {
+ .field = RTE_FLOW_FIELD_TAG,
+ .tag_index = 1,
+ },
+ .b = {
+ .field = RTE_FLOW_FIELD_POINTER,
+ .pvalue = &cmp_value,
+ },
+ .width = 32,
+ };
+ const struct rte_flow_item_compare cmp_mask = {
+ .width = UINT32_MAX,
+ };
+ const struct rte_flow_item_tag tag_spec = {
+ .data = 0x12345678,
+ .index = 3,
+ };
+ const struct rte_flow_item_tag tag_last = {
+ .data = 0xabcdef01,
+ .index = 3,
+ };
+ const struct rte_flow_item_tag tag_mask = {
+ .data = 0x00ff00ff,
+ .index = 0,
+ };
+ const struct rte_flow_item limits[] = {
+ {
+ .type = RTE_FLOW_ITEM_TYPE_IPV4,
+ .spec = &ipv4_open,
+ .last = &ipv4_open,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_IPV4,
+ .spec = &ipv4_spec,
+ .last = &ipv4_last,
+ .mask = &ipv4_mask,
+ },
+ {
+ .type = (enum rte_flow_item_type)-1,
+ .spec = &priv_spec,
+ .mask = &priv_mask,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_GENEVE_OPT,
+ .spec = &opt_spec,
+ .last = &opt_spec,
+ .mask = &opt_mask,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_COMPARE,
+ .spec = &cmp_spec,
+ .mask = &cmp_mask,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_TAG,
+ .spec = &tag_spec,
+ .last = &tag_last,
+ .mask = &tag_mask,
+ },
+ { .type = RTE_FLOW_ITEM_TYPE_END },
+ };
+ union {
+ struct rte_flow_item item;
+ uint8_t buf[1024];
+ } conv;
+ const struct rte_flow_item *out;
+ const struct rte_flow_item_ipv4 *ipv4;
+ const struct rte_flow_item_geneve_opt *opt;
+ const struct rte_flow_item_compare *cmp;
+ const struct rte_flow_item_tag *tag;
+
+ memset(&conv, 0, sizeof(conv));
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, conv.buf,
+ sizeof(conv.buf), limits, NULL);
+ TEST_ASSERT(ret > 0, "Limit pattern conversion failed");
+ TEST_ASSERT((size_t)ret <= sizeof(conv.buf),
+ "Limit pattern conversion needs too much storage");
+
+ out = (const struct rte_flow_item *)conv.buf;
+ TEST_ASSERT_NULL(out[0].mask, "NULL mask must stay NULL");
+ ipv4 = out[0].spec;
+ TEST_ASSERT_NOT_NULL(ipv4, "Unmasked IPv4 spec must be set");
+ TEST_ASSERT_EQUAL(ipv4->hdr.type_of_service, 0xab,
+ "NULL mask must not clear IPv4 TOS");
+ TEST_ASSERT_EQUAL(ipv4->hdr.time_to_live, 0x40,
+ "NULL mask must not clear IPv4 TTL");
+ TEST_ASSERT_EQUAL(ipv4->hdr.src_addr, ipv4_open.hdr.src_addr,
+ "NULL mask must keep IPv4 source address");
+ TEST_ASSERT_EQUAL(ipv4->hdr.dst_addr, ipv4_open.hdr.dst_addr,
+ "NULL mask must keep IPv4 destination address");
+ ipv4 = out[0].last;
+ TEST_ASSERT_NOT_NULL(ipv4, "Unmasked IPv4 last must be set");
+ TEST_ASSERT_EQUAL(ipv4->hdr.type_of_service, 0xab,
+ "NULL mask must not clear IPv4 last TOS");
+ TEST_ASSERT_EQUAL(ipv4->hdr.time_to_live, 0x40,
+ "NULL mask must not clear IPv4 last TTL");
+
+ ipv4 = out[1].spec;
+ TEST_ASSERT_NOT_NULL(ipv4, "Masked IPv4 spec must be set");
+ TEST_ASSERT_EQUAL(ipv4->hdr.type_of_service, 0x0b,
+ "IPv4 spec TOS mask mismatch");
+ TEST_ASSERT_EQUAL(ipv4->hdr.time_to_live, 0,
+ "IPv4 spec TTL must be cleared by the mask");
+ TEST_ASSERT_EQUAL(ipv4->hdr.src_addr, ipv4_spec.hdr.src_addr,
+ "IPv4 spec source address mismatch");
+ TEST_ASSERT_EQUAL(ipv4->hdr.dst_addr, RTE_BE32(0x00000002),
+ "IPv4 spec destination address mismatch");
+ ipv4 = out[1].last;
+ TEST_ASSERT_NOT_NULL(ipv4, "Masked IPv4 last must be set");
+ TEST_ASSERT_EQUAL(ipv4->hdr.type_of_service, 0x0d,
+ "IPv4 last TOS mask mismatch");
+ TEST_ASSERT_EQUAL(ipv4->hdr.time_to_live, 0,
+ "IPv4 last TTL must be cleared by the mask");
+ TEST_ASSERT_EQUAL(ipv4->hdr.src_addr, ipv4_last.hdr.src_addr,
+ "IPv4 last source address mismatch");
+ TEST_ASSERT_EQUAL(ipv4->hdr.dst_addr, RTE_BE32(0x0000ffff),
+ "IPv4 last destination address mismatch");
+
+ TEST_ASSERT_NOT_NULL(out[2].spec, "Private spec must be set");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL(out[2].spec, &priv_spec,
+ sizeof(void *),
+ "PMD private spec must not be masked");
+
+ opt = out[3].spec;
+ TEST_ASSERT_NOT_NULL(opt, "GENEVE_OPT spec must be set");
+ TEST_ASSERT_EQUAL(opt->option_class, 0,
+ "GENEVE_OPT class must be masked");
+ TEST_ASSERT_EQUAL(opt->option_type, 0xaa,
+ "GENEVE_OPT type mask mismatch");
+ TEST_ASSERT_EQUAL(opt->option_len, opt_spec.option_len,
+ "GENEVE_OPT length must not be masked");
+ TEST_ASSERT(opt->data != opt_data,
+ "GENEVE_OPT data must be deep-copied");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL(opt->data, opt_data, sizeof(opt_data),
+ "GENEVE_OPT data must not be masked");
+ opt = out[3].last;
+ TEST_ASSERT_NOT_NULL(opt, "GENEVE_OPT last must be set");
+ TEST_ASSERT_EQUAL(opt->option_class, 0,
+ "GENEVE_OPT last class must be masked");
+ TEST_ASSERT_EQUAL(opt->option_type, 0xaa,
+ "GENEVE_OPT last type mask mismatch");
+ TEST_ASSERT_EQUAL(opt->option_len, opt_spec.option_len,
+ "GENEVE_OPT last length must not be masked");
+ TEST_ASSERT(opt->data != opt_data,
+ "GENEVE_OPT last data must be deep-copied");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL(opt->data, opt_data, sizeof(opt_data),
+ "GENEVE_OPT last data must not be masked");
+
+ cmp = out[4].spec;
+ TEST_ASSERT_NOT_NULL(cmp, "COMPARE spec must be set");
+ TEST_ASSERT_EQUAL(cmp->operation, RTE_FLOW_ITEM_COMPARE_GT,
+ "COMPARE operation must not be masked");
+ TEST_ASSERT_EQUAL(cmp->a.field, RTE_FLOW_FIELD_TAG,
+ "COMPARE field a must not be masked");
+ TEST_ASSERT_EQUAL(cmp->a.tag_index, 1,
+ "COMPARE field a tag index must not be masked");
+ TEST_ASSERT_EQUAL(cmp->b.field, RTE_FLOW_FIELD_POINTER,
+ "COMPARE field b must not be masked");
+ TEST_ASSERT(cmp->b.pvalue == &cmp_value,
+ "COMPARE pointer must not be masked");
+
+ tag = out[5].spec;
+ TEST_ASSERT_NOT_NULL(tag, "TAG spec must be set");
+ TEST_ASSERT_EQUAL(tag->data, 0x00340078,
+ "TAG spec data mask mismatch");
+ TEST_ASSERT_EQUAL(tag->index, tag_spec.index,
+ "TAG spec index must not be masked");
+ tag = out[5].last;
+ TEST_ASSERT_NOT_NULL(tag, "TAG last must be set");
+ TEST_ASSERT_EQUAL(tag->data, 0x00cd0001,
+ "TAG last data mask mismatch");
+ TEST_ASSERT_EQUAL(tag->index, tag_last.index,
+ "TAG last index must not be masked");
+ tag = out[5].mask;
+ TEST_ASSERT_NOT_NULL(tag, "TAG mask must be set");
+ TEST_ASSERT_EQUAL(tag->data, tag_mask.data,
+ "TAG data mask must not change");
+ TEST_ASSERT_EQUAL(tag->index, tag_mask.index,
+ "TAG index mask must not change");
+ }
+
+ return TEST_SUCCESS;
+}
+
static int32_t
ethdev_api_queue_status(void)
{
@@ -162,6 +533,24 @@ ethdev_api_queue_status(void)
return TEST_SUCCESS;
}
+static struct unit_test_suite ethdev_flow_conv_testsuite = {
+ .suite_name = "ethdev flow conversion tests",
+ .setup = NULL,
+ .teardown = NULL,
+ .unit_test_cases = {
+ TEST_CASE(ethdev_api_flow_conv_pattern_masked),
+ TEST_CASES_END()
+ }
+};
+
+static int
+test_ethdev_flow_conv(void)
+{
+ return unit_test_suite_runner(ðdev_flow_conv_testsuite);
+}
+
+REGISTER_FAST_TEST(ethdev_flow_conv, NOHUGE_OK, ASAN_OK, test_ethdev_flow_conv);
+
static struct unit_test_suite ethdev_api_testsuite = {
.suite_name = "ethdev API tests",
.setup = NULL,
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index be758a0c02..c318e961ea 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -123,6 +123,15 @@ New Features
Added internal ``flow_graph`` API in ethdev
for PMD drivers to build graph-based flow pattern parsers.
+* **Added masked pattern conversion to flow API.**
+
+ Added ``RTE_FLOW_CONV_OP_PATTERN_MASKED`` to ``rte_flow_conv()``.
+ The new operation copies a pattern like ``RTE_FLOW_CONV_OP_PATTERN`` and
+ ANDs inline fields of each item ``spec`` and ``last`` with an explicit mask.
+ It preserves ``GENEVE_OPT.option_len`` and ``TAG.index``.
+ RAW pattern bytes, GENEVE_OPT data, FLEX, COMPARE and PMD private items
+ are not masked. Items with a NULL mask remain unchanged.
+
* **Updated AF_XDP driver.**
* Changed the default device plugin endpoint path used when
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 0ebf59165d..25611c712c 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -245,6 +245,37 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)),
};
+/**
+ * Leading spec/last bytes that PATTERN_MASKED may AND with the item mask.
+ *
+ * PMD private items and FLEX return 0: their layout is opaque or the
+ * meaningful bytes sit past a pointer. RAW stops before its pattern
+ * pointer; GENEVE_OPT stops before option_len to preserve the data
+ * length and pointer. TAG stops before index to preserve its selector.
+ * Pointed-to data is not masked. COMPARE returns 0: its field descriptors
+ * hold selectors and pointers (pvalue, flex_handle) rather than match bits.
+ */
+static inline size_t
+rte_flow_conv_item_mask_size(const struct rte_flow_item *item)
+{
+ if ((int)item->type < 0)
+ return 0;
+ switch (item->type) {
+ case RTE_FLOW_ITEM_TYPE_RAW:
+ return offsetof(struct rte_flow_item_raw, pattern);
+ case RTE_FLOW_ITEM_TYPE_GENEVE_OPT:
+ return offsetof(struct rte_flow_item_geneve_opt, option_len);
+ case RTE_FLOW_ITEM_TYPE_TAG:
+ return offsetof(struct rte_flow_item_tag, index);
+ case RTE_FLOW_ITEM_TYPE_COMPARE:
+ return 0;
+ default:
+ if (rte_flow_desc_item[item->type].desc_fn != NULL)
+ return 0;
+ return rte_flow_desc_item[item->type].size;
+ }
+}
+
/** Generate flow_action[] entry. */
#define MK_FLOW_ACTION(t, s) \
[RTE_FLOW_ACTION_TYPE_ ## t] = { \
@@ -887,6 +918,16 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
return off;
}
+/** AND @p len bytes of @p buf with @p mask. */
+static inline void
+rte_flow_conv_apply_mask(uint8_t *buf, const uint8_t *mask, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ buf[i] &= mask[i];
+}
+
/**
* Copy a list of pattern items.
*
@@ -900,6 +941,11 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
* Maximum number of pattern items to process from @p src or 0 to process
* the entire list. In both cases, processing stops after
* RTE_FLOW_ITEM_TYPE_END is encountered.
+ * @param[in] with_mask
+ * If true, AND each copied spec and last with the item mask.
+ * A NULL mask is not replaced by the default mask. RAW pattern bytes,
+ * GENEVE_OPT option_len and data, TAG index, FLEX, COMPARE and PMD
+ * private items are not masked.
* @param[out] error
* Perform verbose error reporting if not NULL.
*
@@ -914,6 +960,7 @@ rte_flow_conv_pattern(struct rte_flow_item *dst,
const size_t size,
const struct rte_flow_item *src,
unsigned int num,
+ bool with_mask,
struct rte_flow_error *error)
{
uintptr_t data = (uintptr_t)dst;
@@ -943,16 +990,29 @@ rte_flow_conv_pattern(struct rte_flow_item *dst,
src -= num;
dst -= num;
do {
+ uint8_t *c_spec = NULL, *c_last = NULL;
+ const uint8_t *mask = NULL;
+ size_t item_mask_size = 0;
+
+ if (with_mask && src->mask) {
+ mask = src->mask;
+ item_mask_size = rte_flow_conv_item_mask_size(src);
+ }
+
if (src->spec) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_item_spec
((void *)(data + off),
size > off ? size - off : 0, src,
RTE_FLOW_CONV_ITEM_SPEC);
- if (size && size >= off + ret)
+ if (size && size >= off + ret) {
dst->spec = (void *)(data + off);
+ c_spec = (uint8_t *)(data + off);
+ }
off += ret;
-
+ if (c_spec && mask && item_mask_size)
+ rte_flow_conv_apply_mask(c_spec, mask,
+ RTE_MIN(ret, item_mask_size));
}
if (src->last) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -960,9 +1020,14 @@ rte_flow_conv_pattern(struct rte_flow_item *dst,
((void *)(data + off),
size > off ? size - off : 0, src,
RTE_FLOW_CONV_ITEM_LAST);
- if (size && size >= off + ret)
+ if (size && size >= off + ret) {
dst->last = (void *)(data + off);
+ c_last = (uint8_t *)(data + off);
+ }
off += ret;
+ if (c_last && mask && item_mask_size)
+ rte_flow_conv_apply_mask(c_last, mask,
+ RTE_MIN(ret, item_mask_size));
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -1109,7 +1174,7 @@ rte_flow_conv_rule(struct rte_flow_conv_rule *dst,
off = RTE_ALIGN_CEIL(off, sizeof(double));
ret = rte_flow_conv_pattern((void *)((uintptr_t)dst + off),
size > off ? size - off : 0,
- src->pattern_ro, 0, error);
+ src->pattern_ro, 0, false, error);
if (ret < 0)
return ret;
if (size && size >= off + (size_t)ret)
@@ -1228,7 +1293,7 @@ rte_flow_conv(enum rte_flow_conv_op op,
ret = sizeof(*attr);
break;
case RTE_FLOW_CONV_OP_ITEM:
- ret = rte_flow_conv_pattern(dst, size, src, 1, error);
+ ret = rte_flow_conv_pattern(dst, size, src, 1, false, error);
break;
case RTE_FLOW_CONV_OP_ITEM_MASK:
item = src;
@@ -1243,7 +1308,7 @@ rte_flow_conv(enum rte_flow_conv_op op,
ret = rte_flow_conv_actions(dst, size, src, 1, error);
break;
case RTE_FLOW_CONV_OP_PATTERN:
- ret = rte_flow_conv_pattern(dst, size, src, 0, error);
+ ret = rte_flow_conv_pattern(dst, size, src, 0, false, error);
break;
case RTE_FLOW_CONV_OP_ACTIONS:
ret = rte_flow_conv_actions(dst, size, src, 0, error);
@@ -1266,6 +1331,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ITEM_DEFAULT_MASK_PTR:
ret = rte_flow_conv_item_default_mask(dst, size, src, error);
break;
+ case RTE_FLOW_CONV_OP_PATTERN_MASKED:
+ ret = rte_flow_conv_pattern(dst, size, src, 0, true, error);
+ break;
default:
ret = rte_flow_error_set
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index df8101e439..3cbe27bac4 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4426,6 +4426,23 @@ enum rte_flow_conv_op {
* @code const void ** @endcode
*/
RTE_FLOW_CONV_OP_ITEM_DEFAULT_MASK_PTR,
+
+ /**
+ * Convert an entire pattern, applying item masks.
+ *
+ * Same as RTE_FLOW_CONV_OP_PATTERN, except each copied spec
+ * and last is ANDed with the item mask. Not masked:
+ * - items with a NULL mask (default mask is not applied);
+ * - RAW pattern and GENEVE_OPT data contents;
+ * - GENEVE_OPT option_len and TAG index;
+ * - FLEX, COMPARE and PMD private items.
+ *
+ * - @p src type:
+ * @code const struct rte_flow_item * @endcode
+ * - @p dst type:
+ * @code struct rte_flow_item * @endcode
+ */
+ RTE_FLOW_CONV_OP_PATTERN_MASKED,
};
/**
--
2.43.0
next prev parent reply other threads:[~2026-10-16 5:27 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-17 7:54 [PATCH 1/2] lib/ethdev: support inline calculating masked item value Bing Zhao
2025-11-17 17:25 ` Stephen Hemminger
2026-02-05 8:45 ` Bing Zhao
2026-02-05 16:42 ` Stephen Hemminger
2026-02-09 4:23 ` Bing Zhao
2026-02-09 14:17 ` Bing Zhao
2026-02-09 18:58 ` Stephen Hemminger
2026-02-09 21:46 ` Thomas Monjalon
2026-02-13 13:31 ` [PATCH v2] " Bing Zhao
2026-02-13 19:50 ` Stephen Hemminger
2026-06-03 8:19 ` [PATCH v3] ethdev: " Bing Zhao
2026-06-03 9:28 ` [PATCH v4] " Bing Zhao
2026-06-08 14:49 ` Dariusz Sosnowski
2026-06-08 15:45 ` Stephen Hemminger
2026-06-09 5:23 ` Bing Zhao
2026-06-10 5:27 ` [PATCH v5] " Bing Zhao
2026-06-10 15:46 ` Stephen Hemminger
2026-06-11 4:55 ` Bing Zhao
2026-06-11 4:56 ` Bing Zhao
2026-10-07 16:57 ` [PATCH v6] " Bing Zhao
2026-10-08 7:17 ` Bing Zhao
2026-10-08 15:27 ` Stephen Hemminger
2026-10-13 13:49 ` [PATCH v7] " Bing Zhao
2026-10-13 15:45 ` Stephen Hemminger
2026-10-16 5:26 ` Bing Zhao [this message]
2026-10-16 16:03 ` [PATCH v8] " Stephen Hemminger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261016052645.288851-1-bingz@nvidia.com \
--to=bingz@nvidia.com \
--cc=dev@dpdk.org \
--cc=dsosnowski@nvidia.com \
--cc=matan@nvidia.com \
--cc=orika@nvidia.com \
--cc=rasland@nvidia.com \
--cc=stephen@networkplumber.org \
--cc=suanmingm@nvidia.com \
--cc=thomas@monjalon.net \
--cc=viacheslavo@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox