[PATCH v8] ethdev: support inline calculating masked item value

Bing Zhao bingz at nvidia.com
Fri Oct 16 07:26:45 CEST 2026


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 at nvidia.com>
Reviewed-by: Dariusz Sosnowski <dsosnowski at 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(&ethdev_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


More information about the dev mailing list