* [PATCH 1/2] lib/ethdev: support inline calculating masked item value
@ 2025-11-17 7:54 Bing Zhao
2025-11-17 17:25 ` Stephen Hemminger
` (2 more replies)
0 siblings, 3 replies; 24+ messages in thread
From: Bing Zhao @ 2025-11-17 7:54 UTC (permalink / raw)
To: viacheslavo, dev, rasland; +Cc: orika, dsosnowski, suanmingm, matan, thomas
In the asynchronous API definition and some drivers, the
rte_flow_item spec value may not be calculated by the driver due to the
reason of speed of light rule insertion rate and sometimes the input
parameters will be copied and changed internally.
After copying, the spec and last will be protected by the keyword
const and cannot be changed in the code itself. And also the driver
needs some extra memory to do the calculation and extra conditions
to understand the length of each item spec. This is not efficient.
To solve the issue and support usage of the following fix, a new OP
was introduced to calculate the spec and last values after applying
the mask inline.
Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
lib/ethdev/rte_flow.c | 29 +++++++++++++++++++++++------
lib/ethdev/rte_flow.h | 12 ++++++++++++
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index fe8f43caff..b94f0e304d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -831,6 +831,8 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
* RTE_FLOW_ITEM_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
+ * @param[in] with_mask
+ * If true, @p src mask will be applied to spec and last.
*
* @return
* A positive value representing the number of bytes needed to store
@@ -843,12 +845,14 @@ 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;
size_t off;
size_t ret;
- unsigned int i;
+ unsigned int i, j;
+ uint8_t *c_spec = NULL, *c_last = NULL;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
/**
@@ -878,9 +882,14 @@ rte_flow_conv_pattern(struct rte_flow_item *dst,
((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 (with_mask && c_spec && src->mask)
+ for (j = 0; j < ret; j++)
+ *(c_spec + j) &= *((const uint8_t *)src->mask + j);
}
if (src->last) {
@@ -889,9 +898,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 (with_mask && c_last && src->mask)
+ for (j = 0; j < ret; j++)
+ *(c_last + j) &= *((const uint8_t *)src->mask + j);
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -1038,7 +1052,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)
@@ -1139,7 +1153,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;
@@ -1154,7 +1168,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);
@@ -1174,6 +1188,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
ret = rte_flow_conv_name(1, 1, 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 3d2ccdeb92..85acb26752 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4559,6 +4559,18 @@ enum rte_flow_conv_op {
* @code const char ** @endcode
*/
RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+
+ /**
+ * Convert an entire pattern.
+ *
+ Duplicates all pattern items at once, applying @p mask to @p spec and @p spec.
+ *
+ * - @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.34.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH 1/2] lib/ethdev: support inline calculating masked item value
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-13 13:31 ` [PATCH v2] " Bing Zhao
2 siblings, 1 reply; 24+ messages in thread
From: Stephen Hemminger @ 2025-11-17 17:25 UTC (permalink / raw)
To: Bing Zhao
Cc: viacheslavo, dev, rasland, orika, dsosnowski, suanmingm, matan,
thomas
On Mon, 17 Nov 2025 09:54:07 +0200
Bing Zhao <bingz@nvidia.com> wrote:
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> index fe8f43caff..b94f0e304d 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -831,6 +831,8 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
> * RTE_FLOW_ITEM_TYPE_END is encountered.
> * @param[out] error
> * Perform verbose error reporting if not NULL.
> + * @param[in] with_mask
> + * If true, @p src mask will be applied to spec and last.
> *
> * @return
> * A positive value representing the number of bytes needed to store
Sorry, this patch is submitted too late to make it into 25.11 release.
Missing any release note.
This would be a good example of where to use API versioning in a later release.
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH 1/2] lib/ethdev: support inline calculating masked item value
2025-11-17 17:25 ` Stephen Hemminger
@ 2026-02-05 8:45 ` Bing Zhao
0 siblings, 0 replies; 24+ messages in thread
From: Bing Zhao @ 2026-02-05 8:45 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh, Ori Kam,
Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
Hi Stephen,
Thanks for the comments. What do you mean by missing release notes? Should I add some description of the new conversion type? It will not break the current usage of any user.
So do you mean I should let the user know that they can do the inline calculation with the new API?
Thanks
> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Tuesday, November 18, 2025 1:25 AM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> Darawsheh <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; Dariusz
> Sosnowski <dsosnowski@nvidia.com>; Suanming Mou <suanmingm@nvidia.com>;
> Matan Azrad <matan@nvidia.com>; NBU-Contact-Thomas Monjalon (EXTERNAL)
> <thomas@monjalon.net>
> Subject: Re: [PATCH 1/2] lib/ethdev: support inline calculating masked
> item value
>
> External email: Use caution opening links or attachments
>
>
> On Mon, 17 Nov 2025 09:54:07 +0200
> Bing Zhao <bingz@nvidia.com> wrote:
>
> > diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c index
> > fe8f43caff..b94f0e304d 100644
> > --- a/lib/ethdev/rte_flow.c
> > +++ b/lib/ethdev/rte_flow.c
> > @@ -831,6 +831,8 @@ rte_flow_conv_action_conf(void *buf, const size_t
> size,
> > * RTE_FLOW_ITEM_TYPE_END is encountered.
> > * @param[out] error
> > * Perform verbose error reporting if not NULL.
> > + * @param[in] with_mask
> > + * If true, @p src mask will be applied to spec and last.
> > *
> > * @return
> > * A positive value representing the number of bytes needed to store
>
> Sorry, this patch is submitted too late to make it into 25.11 release.
> Missing any release note.
>
> This would be a good example of where to use API versioning in a later
> release.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 1/2] lib/ethdev: support inline calculating masked item value
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 16:42 ` Stephen Hemminger
2026-02-09 4:23 ` Bing Zhao
2026-02-13 13:31 ` [PATCH v2] " Bing Zhao
2 siblings, 1 reply; 24+ messages in thread
From: Stephen Hemminger @ 2026-02-05 16:42 UTC (permalink / raw)
To: Bing Zhao
Cc: viacheslavo, dev, rasland, orika, dsosnowski, suanmingm, matan,
thomas
On Mon, 17 Nov 2025 09:54:07 +0200
Bing Zhao <bingz@nvidia.com> wrote:
> In the asynchronous API definition and some drivers, the
> rte_flow_item spec value may not be calculated by the driver due to the
> reason of speed of light rule insertion rate and sometimes the input
> parameters will be copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword
> const and cannot be changed in the code itself. And also the driver
> needs some extra memory to do the calculation and extra conditions
> to understand the length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP
> was introduced to calculate the spec and last values after applying
> the mask inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
Looked at this patch again:
1. No API/ABI breakage adding a single flow type is going
to work great. rte_flow_conv_pattern is internal so changing is no problem.
2. No need for release note for single flow change.
3. Do need a test to cover the new code. I know rte_flow is way under tested
right now. Maybe a chance to use AI to generate a unit test for rte_flow.
This is important because it is a bug trap to add code that is not covered.
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH 1/2] lib/ethdev: support inline calculating masked item value
2026-02-05 16:42 ` Stephen Hemminger
@ 2026-02-09 4:23 ` Bing Zhao
2026-02-09 14:17 ` Bing Zhao
0 siblings, 1 reply; 24+ messages in thread
From: Bing Zhao @ 2026-02-09 4:23 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh, Ori Kam,
Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
Sure. I will add some tests.
> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Friday, February 6, 2026 12:43 AM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> Darawsheh <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; Dariusz
> Sosnowski <dsosnowski@nvidia.com>; Suanming Mou <suanmingm@nvidia.com>;
> Matan Azrad <matan@nvidia.com>; NBU-Contact-Thomas Monjalon (EXTERNAL)
> <thomas@monjalon.net>
> Subject: Re: [PATCH 1/2] lib/ethdev: support inline calculating masked
> item value
>
> External email: Use caution opening links or attachments
>
>
> On Mon, 17 Nov 2025 09:54:07 +0200
> Bing Zhao <bingz@nvidia.com> wrote:
>
> > In the asynchronous API definition and some drivers, the rte_flow_item
> > spec value may not be calculated by the driver due to the reason of
> > speed of light rule insertion rate and sometimes the input parameters
> > will be copied and changed internally.
> >
> > After copying, the spec and last will be protected by the keyword
> > const and cannot be changed in the code itself. And also the driver
> > needs some extra memory to do the calculation and extra conditions to
> > understand the length of each item spec. This is not efficient.
> >
> > To solve the issue and support usage of the following fix, a new OP
> > was introduced to calculate the spec and last values after applying
> > the mask inline.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > ---
>
> Looked at this patch again:
> 1. No API/ABI breakage adding a single flow type is going
> to work great. rte_flow_conv_pattern is internal so changing is no
> problem.
> 2. No need for release note for single flow change.
>
> 3. Do need a test to cover the new code. I know rte_flow is way under
> tested
> right now. Maybe a chance to use AI to generate a unit test for
> rte_flow.
> This is important because it is a bug trap to add code that is not
> covered.
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH 1/2] lib/ethdev: support inline calculating masked item value
2026-02-09 4:23 ` Bing Zhao
@ 2026-02-09 14:17 ` Bing Zhao
2026-02-09 18:58 ` Stephen Hemminger
0 siblings, 1 reply; 24+ messages in thread
From: Bing Zhao @ 2026-02-09 14:17 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh, Ori Kam,
Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
Hi Stephen,
Since there is no UT for the flow API. Such application would require some design discussions, and it is too short for -rc1. Could we focus on the change itself and try to close it? Thanks a lot.
> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Monday, February 9, 2026 12:24 PM
> To: Stephen Hemminger <stephen@networkplumber.org>
> Cc: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> Darawsheh <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; Dariusz
> Sosnowski <dsosnowski@nvidia.com>; Suanming Mou <suanmingm@nvidia.com>;
> Matan Azrad <matan@nvidia.com>; NBU-Contact-Thomas Monjalon (EXTERNAL)
> <thomas@monjalon.net>
> Subject: RE: [PATCH 1/2] lib/ethdev: support inline calculating masked
> item value
>
> Sure. I will add some tests.
>
> > -----Original Message-----
> > From: Stephen Hemminger <stephen@networkplumber.org>
> > Sent: Friday, February 6, 2026 12:43 AM
> > To: Bing Zhao <bingz@nvidia.com>
> > Cc: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> > Darawsheh <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; Dariusz
> > Sosnowski <dsosnowski@nvidia.com>; Suanming Mou
> > <suanmingm@nvidia.com>; Matan Azrad <matan@nvidia.com>;
> > NBU-Contact-Thomas Monjalon (EXTERNAL) <thomas@monjalon.net>
> > Subject: Re: [PATCH 1/2] lib/ethdev: support inline calculating masked
> > item value
> >
> > External email: Use caution opening links or attachments
> >
> >
> > On Mon, 17 Nov 2025 09:54:07 +0200
> > Bing Zhao <bingz@nvidia.com> wrote:
> >
> > > In the asynchronous API definition and some drivers, the
> > > rte_flow_item spec value may not be calculated by the driver due to
> > > the reason of speed of light rule insertion rate and sometimes the
> > > input parameters will be copied and changed internally.
> > >
> > > After copying, the spec and last will be protected by the keyword
> > > const and cannot be changed in the code itself. And also the driver
> > > needs some extra memory to do the calculation and extra conditions
> > > to understand the length of each item spec. This is not efficient.
> > >
> > > To solve the issue and support usage of the following fix, a new OP
> > > was introduced to calculate the spec and last values after applying
> > > the mask inline.
> > >
> > > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > > ---
> >
> > Looked at this patch again:
> > 1. No API/ABI breakage adding a single flow type is going
> > to work great. rte_flow_conv_pattern is internal so changing is
> > no problem.
> > 2. No need for release note for single flow change.
> >
> > 3. Do need a test to cover the new code. I know rte_flow is way
> > under tested
> > right now. Maybe a chance to use AI to generate a unit test for
> > rte_flow.
> > This is important because it is a bug trap to add code that is
> > not covered.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 1/2] lib/ethdev: support inline calculating masked item value
2026-02-09 14:17 ` Bing Zhao
@ 2026-02-09 18:58 ` Stephen Hemminger
2026-02-09 21:46 ` Thomas Monjalon
0 siblings, 1 reply; 24+ messages in thread
From: Stephen Hemminger @ 2026-02-09 18:58 UTC (permalink / raw)
To: Bing Zhao
Cc: Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh, Ori Kam,
Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
On Mon, 9 Feb 2026 14:17:27 +0000
Bing Zhao <bingz@nvidia.com> wrote:
> Hi Stephen,
>
> Since there is no UT for the flow API. Such application would require some design discussions, and it is too short for -rc1. Could we focus on the change itself and try to close it? Thanks a lot.
I have discovered that AI does pretty good job of generating basic tests.
That is all we need to get some basic CI coverage.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH 1/2] lib/ethdev: support inline calculating masked item value
2026-02-09 18:58 ` Stephen Hemminger
@ 2026-02-09 21:46 ` Thomas Monjalon
0 siblings, 0 replies; 24+ messages in thread
From: Thomas Monjalon @ 2026-02-09 21:46 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Bing Zhao, dev, Slava Ovsiienko, Raslan Darawsheh, Ori Kam,
Dariusz Sosnowski, Suanming Mou, Matan Azrad
09/02/2026 19:58, Stephen Hemminger:
> On Mon, 9 Feb 2026 14:17:27 +0000
> Bing Zhao <bingz@nvidia.com> wrote:
>
> > Hi Stephen,
> >
> > Since there is no UT for the flow API.
> > Such application would require some design discussions,
> > and it is too short for -rc1.
> > Could we focus on the change itself and try to close it? Thanks a lot.
>
> I have discovered that AI does pretty good job of generating basic tests.
> That is all we need to get some basic CI coverage.
As you know, we don't have a stub implementation working without specific HW.
So far, the decision discussed in the techboard was to implement flow API tests
in DTS and run it in UNH. This work is in progress in the DTS team.
I don't think we should require something else at this point.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v2] lib/ethdev: support inline calculating masked item value
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 16:42 ` Stephen Hemminger
@ 2026-02-13 13:31 ` Bing Zhao
2026-02-13 19:50 ` Stephen Hemminger
2026-06-03 8:19 ` [PATCH v3] ethdev: " Bing Zhao
2 siblings, 2 replies; 24+ messages in thread
From: Bing Zhao @ 2026-02-13 13:31 UTC (permalink / raw)
To: viacheslavo, dev, rasland
Cc: orika, dsosnowski, suanmingm, matan, thomas, stephen
In the asynchronous API definition and some drivers, the
rte_flow_item spec value may not be calculated by the driver due to the
reason of speed of light rule insertion rate and sometimes the input
parameters will be copied and changed internally.
After copying, the spec and last will be protected by the keyword
const and cannot be changed in the code itself. And also the driver
needs some extra memory to do the calculation and extra conditions
to understand the length of each item spec. This is not efficient.
To solve the issue and support usage of the following fix, a new OP
was introduced to calculate the spec and last values after applying
the mask inline.
Signed-off-by: Bing Zhao <bingz@nvidia.com>
Reviewed-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
lib/ethdev/rte_flow.c | 29 +++++++++++++++++++++++------
lib/ethdev/rte_flow.h | 12 ++++++++++++
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index fe8f43caff..b94f0e304d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -831,6 +831,8 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
* RTE_FLOW_ITEM_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
+ * @param[in] with_mask
+ * If true, @p src mask will be applied to spec and last.
*
* @return
* A positive value representing the number of bytes needed to store
@@ -843,12 +845,14 @@ 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;
size_t off;
size_t ret;
- unsigned int i;
+ unsigned int i, j;
+ uint8_t *c_spec = NULL, *c_last = NULL;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
/**
@@ -878,9 +882,14 @@ rte_flow_conv_pattern(struct rte_flow_item *dst,
((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 (with_mask && c_spec && src->mask)
+ for (j = 0; j < ret; j++)
+ *(c_spec + j) &= *((const uint8_t *)src->mask + j);
}
if (src->last) {
@@ -889,9 +898,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 (with_mask && c_last && src->mask)
+ for (j = 0; j < ret; j++)
+ *(c_last + j) &= *((const uint8_t *)src->mask + j);
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -1038,7 +1052,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)
@@ -1139,7 +1153,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;
@@ -1154,7 +1168,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);
@@ -1174,6 +1188,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
ret = rte_flow_conv_name(1, 1, 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 ba3bcc89a3..f56b328537 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4558,6 +4558,18 @@ enum rte_flow_conv_op {
* @code const char ** @endcode
*/
RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+
+ /**
+ * Convert an entire pattern.
+ *
+ Duplicates all pattern items at once, applying @p mask to @p spec and @p spec.
+ *
+ * - @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.34.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v2] lib/ethdev: support inline calculating masked item value
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
1 sibling, 0 replies; 24+ messages in thread
From: Stephen Hemminger @ 2026-02-13 19:50 UTC (permalink / raw)
To: Bing Zhao
Cc: viacheslavo, dev, rasland, orika, dsosnowski, suanmingm, matan,
thomas
On Fri, 13 Feb 2026 15:31:43 +0200
Bing Zhao <bingz@nvidia.com> wrote:
> In the asynchronous API definition and some drivers, the
> rte_flow_item spec value may not be calculated by the driver due to the
> reason of speed of light rule insertion rate and sometimes the input
> parameters will be copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword
> const and cannot be changed in the code itself. And also the driver
> needs some extra memory to do the calculation and extra conditions
> to understand the length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP
> was introduced to calculate the spec and last values after applying
> the mask inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Reviewed-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
I didn't see any direct issue, but AI deep dive did.
Please consider these and resubmit (or say why they are false positives).
PS: I would just pull the Doxygen comment, this is internal and never gets
processed for inclusion in doc.
Summary
Severity Finding
Error Stale c_spec/c_last pointers across loop iterations — can corrupt previously copied item data
Error Potential out-of-bounds read on src->mask when ret exceeds actual mask size
Error Wrong component prefix (lib/ethdev: → ethdev:)
Error Broken Doxygen comment (missing * prefix) and typo (spec → last)
Warning New enum value not marked experimental
Warning Missing release notes for API change
Warning Missing functional tests
Info Pointer arithmetic vs array indexing style
---
## Deep Dive Review: `[PATCH v2] lib/ethdev: support inline calculating masked item value`
### Commit Message Analysis
**Subject line:** `lib/ethdev: support inline calculating masked item value`
**Error — Wrong component prefix.** The subject uses `lib/ethdev:` but per AGENTS.md §Common Prefix Mistakes, the correct prefix for ethdev is `ethdev:` (without `lib/`). Should be:
```
ethdev: support inline calculating masked item value
```
Let me verify the character count of the current subject: `lib/ethdev: support inline calculating masked item value` = 56 characters — within the 60-character limit, but after fixing the prefix to `ethdev:` it would be shorter still.
**Commit body** — the description is functional but uses somewhat unclear phrasing: "the reason of speed of light rule insertion rate" is vague and would benefit from clearer technical justification. However, per AGENTS.md guidance, I'll only flag clarity issues if genuinely confusing — this is borderline but understandable in context to a flow API developer.
**Tags:** Signed-off-by and Reviewed-by are present and in correct order (Group 2 tags only, no Group 1 tags needed). No issues there.
---
### Correctness Bugs (Highest Priority)
**1. Error — `c_spec` / `c_last` not reset per loop iteration: stale pointer may cause masking wrong item's data (~80% confidence)**
The variables `c_spec` and `c_last` are declared outside the `for` loop and initialized to `NULL`, but they are **never reset to `NULL`** at the start of each iteration. Consider this scenario:
- Iteration N: `src->spec` exists, `size` is sufficient, so `c_spec` is set to a valid pointer. The masking is applied correctly.
- Iteration N+1: `src->spec` exists, but `size` is *not* sufficient (the `size && size >= off + ret` check fails). `c_spec` retains the pointer from iteration N. Then the `with_mask && c_spec && src->mask` check passes, and the masking loop applies the **new item's mask** to the **previous item's already-copied spec data**, corrupting it.
The same issue applies to `c_last`.
**Suggested fix:** Reset both pointers at the top of each loop iteration, or (better) scope them inside the relevant `if` blocks:
```c
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
uint8_t *c_spec = NULL, *c_last = NULL;
/* ... rest of loop body ... */
```
**2. Error — Masking applied to converted data using `ret` as length, but `ret` includes alignment padding (~60% confidence)**
The return value `ret` from `rte_flow_conv_pattern` internal call via `rte_flow_conv_item_spec` may include padding from `RTE_ALIGN_CEIL`. The masking loop uses `ret` as the byte count:
```c
for (j = 0; j < ret; j++)
*(c_spec + j) &= *((const uint8_t *)src->mask + j);
```
If `ret` is larger than the actual item spec size, this reads past the end of `src->mask`, which may only be `sizeof(struct rte_flow_item_<type>)` bytes. This is a potential **out-of-bounds read** on `src->mask`.
The mask is user-provided via `src->mask` and its actual size corresponds to the item type, not the aligned/padded conversion size. If `ret` exceeds the true item spec size, the extra bytes of `src->mask` are being read from unknown memory.
**Suggested fix:** The masking should use the actual item spec size rather than the conversion return value, or at minimum clamp the loop to `min(ret, item_spec_size)`.
**3. Warning — No bounds validation between `ret` and mask size (~50% confidence)**
Related to the above: there's no assertion or runtime check that `src->mask` points to at least `ret` bytes of valid data. The `rte_flow_conv` infrastructure knows the item type sizes, but this masking code makes an assumption that the mask buffer is at least as large as the converted spec. If a caller provides a shorter mask, this is undefined behavior.
---
### API / Process Issues
**4. Warning — New public enum value `RTE_FLOW_CONV_OP_PATTERN_MASKED` not marked experimental.**
A new value is added to the public `enum rte_flow_conv_op` in `rte_flow.h`. Per AGENTS.md, new APIs must be marked as `__rte_experimental`. While enum values can't directly carry the `__rte_experimental` tag, this is a new public API feature exposed through `rte_flow_conv()` and should be documented as experimental, or the corresponding code path should have experimental gating.
**5. Warning — Missing release notes.**
This patch adds a new conversion operation (`RTE_FLOW_CONV_OP_PATTERN_MASKED`) to the public `rte_flow_conv()` API. Per AGENTS.md, API changes require release notes updates in `doc/guides/rel_notes/`.
**6. Warning — Missing tests.**
No test code is included for the new `RTE_FLOW_CONV_OP_PATTERN_MASKED` operation. Per AGENTS.md, new API functions/operations should have functional tests.
---
### Documentation Issues
**7. Error — Doxygen comment has broken formatting and a typo.**
In `rte_flow.h`, the new enum doc block:
```c
/**
* Convert an entire pattern.
*
Duplicates all pattern items at once, applying @p mask to @p spec and @p spec.
```
Two problems:
- The line `Duplicates all pattern items at once...` is missing the leading ` *` comment continuation prefix — this will break Doxygen parsing.
- The text says "applying @p mask to @p spec and @p spec" — the second `@p spec` should be `@p last`.
**Suggested fix:**
```c
/**
* Convert an entire pattern.
*
* Duplicates all pattern items at once, applying @p mask to @p spec and @p last.
```
---
### Style Observations
**8. Info — Pointer arithmetic style.**
The masking uses `*(c_spec + j)` rather than the more idiomatic `c_spec[j]`, and `*((const uint8_t *)src->mask + j)` rather than `((const uint8_t *)src->mask)[j]`. Array indexing notation would be clearer and more consistent with typical DPDK style.
---
### Summary
| Severity | Finding |
|----------|---------|
| **Error** | Stale `c_spec`/`c_last` pointers across loop iterations — can corrupt previously copied item data |
| **Error** | Potential out-of-bounds read on `src->mask` when `ret` exceeds actual mask size |
| **Error** | Wrong component prefix (`lib/ethdev:` → `ethdev:`) |
| **Error** | Broken Doxygen comment (missing `*` prefix) and typo (`spec` → `last`) |
| **Warning** | New enum value not marked experimental |
| **Warning** | Missing release notes for API change |
| **Warning** | Missing functional tests |
| **Info** | Pointer arithmetic vs array indexing style |
The most critical finding is #1 — the stale pointer bug. It's a logic error that would cause data corruption whenever `rte_flow_conv` is called with `RTE_FLOW_CONV_OP_PATTERN_MASKED` on a pattern containing multiple items where the output buffer is too small for some but not all items.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v3] ethdev: support inline calculating masked item value
2026-02-13 13:31 ` [PATCH v2] " Bing Zhao
2026-02-13 19:50 ` Stephen Hemminger
@ 2026-06-03 8:19 ` Bing Zhao
2026-06-03 9:28 ` [PATCH v4] " Bing Zhao
1 sibling, 1 reply; 24+ messages in thread
From: Bing Zhao @ 2026-06-03 8:19 UTC (permalink / raw)
To: viacheslavo, dev, rasland, stephen
Cc: orika, dsosnowski, suanmingm, matan, thomas
In the asynchronous API definition and some drivers, the
rte_flow_item spec value may not be calculated by the driver due to the
reason of speed of light rule insertion rate and sometimes the input
parameters will be copied and changed internally.
After copying, the spec and last will be protected by the keyword
const and cannot be changed in the code itself. And also the driver
needs some extra memory to do the calculation and extra conditions
to understand the length of each item spec. This is not efficient.
To solve the issue and support usage of the following fix, a new OP
was introduced to calculate the spec and last values after applying
the mask inline.
Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
v3:
- add test code
- fix the issue found by AI
---
app/test/test_ethdev_api.c | 76 ++++++++++++++++++++++++++
doc/guides/rel_notes/release_26_07.rst | 6 ++
lib/ethdev/rte_flow.c | 46 ++++++++++++++--
lib/ethdev/rte_flow.h | 13 +++++
4 files changed, 135 insertions(+), 6 deletions(-)
diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c
index 76afd0345c..5cae1cdc1d 100644
--- a/app/test/test_ethdev_api.c
+++ b/app/test/test_ethdev_api.c
@@ -4,6 +4,7 @@
#include <rte_log.h>
#include <rte_ethdev.h>
+#include <rte_flow.h>
#include <rte_test.h>
#include "test.h"
@@ -15,6 +16,80 @@
#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 struct rte_flow_item pattern[] = {
+ {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .spec = &spec,
+ .last = &last,
+ .mask = &mask,
+ },
+ { .type = RTE_FLOW_ITEM_TYPE_END },
+ };
+ union {
+ struct rte_flow_item item;
+ struct rte_flow_item_eth eth;
+ double align;
+ uint8_t raw[256];
+ } dst;
+ const struct rte_flow_item *item;
+ const struct rte_flow_item_eth *conv_spec;
+ const struct rte_flow_item_eth *conv_last;
+ 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.raw),
+ "Masked pattern conversion needs too much storage");
+
+ memset(&dst, 0, sizeof(dst));
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, dst.raw,
+ sizeof(dst.raw), pattern, NULL);
+ TEST_ASSERT(ret > 0, "Masked pattern conversion failed");
+
+ item = (const struct rte_flow_item *)dst.raw;
+ 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");
+
+ return TEST_SUCCESS;
+}
+
static int32_t
ethdev_api_queue_status(void)
{
@@ -167,6 +242,7 @@ static struct unit_test_suite ethdev_api_testsuite = {
.setup = NULL,
.teardown = NULL,
.unit_test_cases = {
+ TEST_CASE(ethdev_api_flow_conv_pattern_masked),
TEST_CASE(ethdev_api_queue_status),
/* TODO: Add deferred_start queue status test */
TEST_CASES_END() /**< NULL terminate unit test array */
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 8b4f8401e2..46a83cfea5 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -116,6 +116,12 @@ API Changes
Also, make sure to start the actual text at the margin.
=======================================================
+* ethdev: Added masked pattern conversion.
+
+ Added ``RTE_FLOW_CONV_OP_PATTERN_MASKED`` to ``rte_flow_conv()``
+ to copy an entire pattern while applying each item's mask to its
+ ``spec`` and ``last`` fields.
+
ABI Changes
-----------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7a51b667cf..7cf9f6f6f3 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -178,6 +178,14 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)),
};
+static inline size_t
+rte_flow_conv_item_mask_size(const struct rte_flow_item *item)
+{
+ if ((int)item->type >= 0)
+ return rte_flow_desc_item[item->type].size;
+ return sizeof(void *);
+}
+
/** Generate flow_action[] entry. */
#define MK_FLOW_ACTION(t, s) \
[RTE_FLOW_ACTION_TYPE_ ## t] = { \
@@ -835,6 +843,8 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
* RTE_FLOW_ITEM_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
+ * @param[in] with_mask
+ * If true, @p src mask will be applied to spec and last.
*
* @return
* A positive value representing the number of bytes needed to store
@@ -847,12 +857,13 @@ 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;
size_t off;
size_t ret;
- unsigned int i;
+ unsigned int i, j;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
/**
@@ -876,15 +887,27 @@ 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 = src->mask;
+ size_t item_mask_size = mask ? rte_flow_conv_item_mask_size(src) : 0;
+
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 (with_mask && c_spec && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_spec[j] &= mask[j];
+ }
}
if (src->last) {
@@ -893,9 +916,17 @@ 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 (with_mask && c_last && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_last[j] &= mask[j];
+ }
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -1042,7 +1073,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)
@@ -1143,7 +1174,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;
@@ -1158,7 +1189,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);
@@ -1178,6 +1209,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
ret = rte_flow_conv_name(1, 1, 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 ba3bcc89a3..f3102f4bb0 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4558,6 +4558,19 @@ enum rte_flow_conv_op {
* @code const char ** @endcode
*/
RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+
+ /**
+ * Convert an entire pattern.
+ *
+ * Duplicates all pattern items at once, applying @p mask to @p spec
+ * and @p last.
+ *
+ * - @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.34.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v4] ethdev: support inline calculating masked item value
2026-06-03 8:19 ` [PATCH v3] ethdev: " Bing Zhao
@ 2026-06-03 9:28 ` Bing Zhao
2026-06-08 14:49 ` Dariusz Sosnowski
` (2 more replies)
0 siblings, 3 replies; 24+ messages in thread
From: Bing Zhao @ 2026-06-03 9:28 UTC (permalink / raw)
To: viacheslavo, dev, rasland, stephen
Cc: orika, dsosnowski, suanmingm, matan, thomas
In the asynchronous API definition and some drivers, the
rte_flow_item spec value may not be calculated by the driver due to the
reason of speed of light rule insertion rate and sometimes the input
parameters will be copied and changed internally.
After copying, the spec and last will be protected by the keyword
const and cannot be changed in the code itself. And also the driver
needs some extra memory to do the calculation and extra conditions
to understand the length of each item spec. This is not efficient.
To solve the issue and support usage of the following fix, a new OP
was introduced to calculate the spec and last values after applying
the mask inline.
Signed-off-by: Bing Zhao <bingz@nvidia.com>
---
v3:
- add test code
- fix the issue found by AI
v4: reabse on top of the main
---
app/test/test_ethdev_api.c | 76 ++++++++++++++++++++++++++
doc/guides/rel_notes/release_26_07.rst | 6 ++
lib/ethdev/rte_flow.c | 46 ++++++++++++++--
lib/ethdev/rte_flow.h | 13 +++++
4 files changed, 135 insertions(+), 6 deletions(-)
diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c
index 76afd0345c..5cae1cdc1d 100644
--- a/app/test/test_ethdev_api.c
+++ b/app/test/test_ethdev_api.c
@@ -4,6 +4,7 @@
#include <rte_log.h>
#include <rte_ethdev.h>
+#include <rte_flow.h>
#include <rte_test.h>
#include "test.h"
@@ -15,6 +16,80 @@
#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 struct rte_flow_item pattern[] = {
+ {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .spec = &spec,
+ .last = &last,
+ .mask = &mask,
+ },
+ { .type = RTE_FLOW_ITEM_TYPE_END },
+ };
+ union {
+ struct rte_flow_item item;
+ struct rte_flow_item_eth eth;
+ double align;
+ uint8_t raw[256];
+ } dst;
+ const struct rte_flow_item *item;
+ const struct rte_flow_item_eth *conv_spec;
+ const struct rte_flow_item_eth *conv_last;
+ 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.raw),
+ "Masked pattern conversion needs too much storage");
+
+ memset(&dst, 0, sizeof(dst));
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, dst.raw,
+ sizeof(dst.raw), pattern, NULL);
+ TEST_ASSERT(ret > 0, "Masked pattern conversion failed");
+
+ item = (const struct rte_flow_item *)dst.raw;
+ 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");
+
+ return TEST_SUCCESS;
+}
+
static int32_t
ethdev_api_queue_status(void)
{
@@ -167,6 +242,7 @@ static struct unit_test_suite ethdev_api_testsuite = {
.setup = NULL,
.teardown = NULL,
.unit_test_cases = {
+ TEST_CASE(ethdev_api_flow_conv_pattern_masked),
TEST_CASE(ethdev_api_queue_status),
/* TODO: Add deferred_start queue status test */
TEST_CASES_END() /**< NULL terminate unit test array */
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index b8a3e2ced9..5f01420d2b 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -134,6 +134,12 @@ API Changes
Also, make sure to start the actual text at the margin.
=======================================================
+* ethdev: Added masked pattern conversion.
+
+ Added ``RTE_FLOW_CONV_OP_PATTERN_MASKED`` to ``rte_flow_conv()``
+ to copy an entire pattern while applying each item's mask to its
+ ``spec`` and ``last`` fields.
+
ABI Changes
-----------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7a51b667cf..7cf9f6f6f3 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -178,6 +178,14 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)),
};
+static inline size_t
+rte_flow_conv_item_mask_size(const struct rte_flow_item *item)
+{
+ if ((int)item->type >= 0)
+ return rte_flow_desc_item[item->type].size;
+ return sizeof(void *);
+}
+
/** Generate flow_action[] entry. */
#define MK_FLOW_ACTION(t, s) \
[RTE_FLOW_ACTION_TYPE_ ## t] = { \
@@ -835,6 +843,8 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
* RTE_FLOW_ITEM_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
+ * @param[in] with_mask
+ * If true, @p src mask will be applied to spec and last.
*
* @return
* A positive value representing the number of bytes needed to store
@@ -847,12 +857,13 @@ 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;
size_t off;
size_t ret;
- unsigned int i;
+ unsigned int i, j;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
/**
@@ -876,15 +887,27 @@ 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 = src->mask;
+ size_t item_mask_size = mask ? rte_flow_conv_item_mask_size(src) : 0;
+
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 (with_mask && c_spec && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_spec[j] &= mask[j];
+ }
}
if (src->last) {
@@ -893,9 +916,17 @@ 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 (with_mask && c_last && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_last[j] &= mask[j];
+ }
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -1042,7 +1073,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)
@@ -1143,7 +1174,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;
@@ -1158,7 +1189,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);
@@ -1178,6 +1209,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
ret = rte_flow_conv_name(1, 1, 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 eacfd7c5f7..5f8d4b00f0 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4558,6 +4558,19 @@ enum rte_flow_conv_op {
* @code const char ** @endcode
*/
RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+
+ /**
+ * Convert an entire pattern.
+ *
+ * Duplicates all pattern items at once, applying @p mask to @p spec
+ * and @p last.
+ *
+ * - @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.34.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v4] ethdev: support inline calculating masked item value
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-10 5:27 ` [PATCH v5] " Bing Zhao
2 siblings, 0 replies; 24+ messages in thread
From: Dariusz Sosnowski @ 2026-06-08 14:49 UTC (permalink / raw)
To: Bing Zhao, orika
Cc: viacheslavo, dev, rasland, stephen, suanmingm, matan, thomas
On Wed, Jun 03, 2026 at 12:28:05PM +0300, Bing Zhao wrote:
> In the asynchronous API definition and some drivers, the
> rte_flow_item spec value may not be calculated by the driver due to the
> reason of speed of light rule insertion rate and sometimes the input
> parameters will be copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword
> const and cannot be changed in the code itself. And also the driver
> needs some extra memory to do the calculation and extra conditions
> to understand the length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP
> was introduced to calculate the spec and last values after applying
> the mask inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
Ori has some technical issues with plain text emails on his side.
On his behalf:
Acked-by: Ori Kam <orika@nvidia.com>
Best regards,
Dariusz Sosnowski
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v4] ethdev: support inline calculating masked item value
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
2 siblings, 1 reply; 24+ messages in thread
From: Stephen Hemminger @ 2026-06-08 15:45 UTC (permalink / raw)
To: Bing Zhao
Cc: viacheslavo, dev, rasland, orika, dsosnowski, suanmingm, matan,
thomas
On Wed, 3 Jun 2026 12:28:05 +0300
Bing Zhao <bingz@nvidia.com> wrote:
> In the asynchronous API definition and some drivers, the
> rte_flow_item spec value may not be calculated by the driver due to the
> reason of speed of light rule insertion rate and sometimes the input
> parameters will be copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword
> const and cannot be changed in the code itself. And also the driver
> needs some extra memory to do the calculation and extra conditions
> to understand the length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP
> was introduced to calculate the spec and last values after applying
> the mask inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> ---
More detailed AI review found some things that still need addressing.
On Wed, 3 Jun 2026 12:28:05 +0300, Bing Zhao wrote:
> Subject: [PATCH v4] ethdev: support inline calculating masked item value
Error: byte-wise masking corrupts embedded pointers in deep-copy item
types (RAW, FLEX, GENEVE_OPT).
In rte_flow_conv_pattern(), the new mask application runs over the fixed
item struct:
size_t item_mask_size = mask ? rte_flow_conv_item_mask_size(src) : 0;
...
size_t mask_size = RTE_MIN(ret, item_mask_size);
for (j = 0; j < mask_size; j++)
c_spec[j] &= mask[j];
item_mask_size is rte_flow_desc_item[type].size, the size of the fixed
item struct. For RTE_FLOW_ITEM_TYPE_RAW, FLEX, and GENEVE_OPT, that fixed
struct ends in an embedded pointer that rte_flow_conv_item_spec() has just
populated to point at the deep-copied trailing data (rte_flow_item_raw.pattern,
rte_flow_item_flex.pattern, rte_flow_item_geneve_opt.data). Because the masked
range covers the whole fixed struct, the loop ANDs the bytes of that pointer
with the mask's corresponding bytes (typically a NULL mask pointer), zeroing
or garbling it.
The converted item's pattern/data pointer is clobbered while the copied
payload it should reference is left unreachable. A consumer that follows
conv->pattern then dereferences NULL or a corrupt address. Plain value items
(eth, ipv4, ...) are unaffected; only the deep-copy item types break, which
is exactly what the test does not exercise.
Suggested fix: do not blind-mask the entire fixed struct for items that carry
an embedded pointer / desc_fn deep copy. Either skip masking when
rte_flow_desc_item[type].desc_fn != NULL, or mask only the leading plain-data
region and leave the pointer field (and trailing copied bytes) intact.
Warning: the new test validates only an ETH pattern, so the RAW/FLEX/GENEVE_OPT
path above is untested. A RAW item case would have surfaced the pointer
corruption.
Info: the Doxygen block for RTE_FLOW_CONV_OP_PATTERN_MASKED uses @p mask,
@p spec, @p last, but those are item fields, not parameters of the op; the
neighboring enum entries only document the @p src / @p dst types.
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH v4] ethdev: support inline calculating masked item value
2026-06-08 15:45 ` Stephen Hemminger
@ 2026-06-09 5:23 ` Bing Zhao
0 siblings, 0 replies; 24+ messages in thread
From: Bing Zhao @ 2026-06-09 5:23 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh, Ori Kam,
Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
Hi,
Let me check and update with v5.
> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Monday, June 8, 2026 11:46 PM
> To: Bing Zhao <bingz@nvidia.com>
> Cc: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> Darawsheh <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; Dariusz
> Sosnowski <dsosnowski@nvidia.com>; Suanming Mou <suanmingm@nvidia.com>;
> Matan Azrad <matan@nvidia.com>; NBU-Contact-Thomas Monjalon (EXTERNAL)
> <thomas@monjalon.net>
> Subject: Re: [PATCH v4] ethdev: support inline calculating masked item
> value
>
> External email: Use caution opening links or attachments
>
>
> On Wed, 3 Jun 2026 12:28:05 +0300
> Bing Zhao <bingz@nvidia.com> wrote:
>
> > In the asynchronous API definition and some drivers, the rte_flow_item
> > spec value may not be calculated by the driver due to the reason of
> > speed of light rule insertion rate and sometimes the input parameters
> > will be copied and changed internally.
> >
> > After copying, the spec and last will be protected by the keyword
> > const and cannot be changed in the code itself. And also the driver
> > needs some extra memory to do the calculation and extra conditions to
> > understand the length of each item spec. This is not efficient.
> >
> > To solve the issue and support usage of the following fix, a new OP
> > was introduced to calculate the spec and last values after applying
> > the mask inline.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > ---
>
> More detailed AI review found some things that still need addressing.
>
> On Wed, 3 Jun 2026 12:28:05 +0300, Bing Zhao wrote:
> > Subject: [PATCH v4] ethdev: support inline calculating masked item
> > value
>
> Error: byte-wise masking corrupts embedded pointers in deep-copy item
> types (RAW, FLEX, GENEVE_OPT).
>
> In rte_flow_conv_pattern(), the new mask application runs over the fixed
> item struct:
>
> size_t item_mask_size = mask ? rte_flow_conv_item_mask_size(src) :
> 0;
> ...
> size_t mask_size = RTE_MIN(ret, item_mask_size);
>
> for (j = 0; j < mask_size; j++)
> c_spec[j] &= mask[j];
>
> item_mask_size is rte_flow_desc_item[type].size, the size of the fixed
> item struct. For RTE_FLOW_ITEM_TYPE_RAW, FLEX, and GENEVE_OPT, that fixed
> struct ends in an embedded pointer that rte_flow_conv_item_spec() has just
> populated to point at the deep-copied trailing data
> (rte_flow_item_raw.pattern, rte_flow_item_flex.pattern,
> rte_flow_item_geneve_opt.data). Because the masked range covers the whole
> fixed struct, the loop ANDs the bytes of that pointer with the mask's
> corresponding bytes (typically a NULL mask pointer), zeroing or garbling
> it.
>
> The converted item's pattern/data pointer is clobbered while the copied
> payload it should reference is left unreachable. A consumer that follows
> conv->pattern then dereferences NULL or a corrupt address. Plain value
> conv->items
> (eth, ipv4, ...) are unaffected; only the deep-copy item types break,
> which is exactly what the test does not exercise.
>
> Suggested fix: do not blind-mask the entire fixed struct for items that
> carry an embedded pointer / desc_fn deep copy. Either skip masking when
> rte_flow_desc_item[type].desc_fn != NULL, or mask only the leading plain-
> data region and leave the pointer field (and trailing copied bytes)
> intact.
>
> Warning: the new test validates only an ETH pattern, so the
> RAW/FLEX/GENEVE_OPT path above is untested. A RAW item case would have
> surfaced the pointer corruption.
>
> Info: the Doxygen block for RTE_FLOW_CONV_OP_PATTERN_MASKED uses @p mask,
> @p spec, @p last, but those are item fields, not parameters of the op; the
> neighboring enum entries only document the @p src / @p dst types.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v5] ethdev: support inline calculating masked item value
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-10 5:27 ` Bing Zhao
2026-06-10 15:46 ` Stephen Hemminger
` (2 more replies)
2 siblings, 3 replies; 24+ messages in thread
From: Bing Zhao @ 2026-06-10 5:27 UTC (permalink / raw)
To: viacheslavo, dev, rasland, stephen
Cc: orika, dsosnowski, suanmingm, matan, thomas
In the asynchronous API definition and some drivers, the
rte_flow_item spec value may not be calculated by the driver due to the
reason of speed of light rule insertion rate and sometimes the input
parameters will be copied and changed internally.
After copying, the spec and last will be protected by the keyword
const and cannot be changed in the code itself. And also the driver
needs some extra memory to do the calculation and extra conditions
to understand the length of each item spec. This is not efficient.
To solve the issue and support usage of the following fix, a new OP
was introduced to calculate the spec and last values after applying
the mask inline.
Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
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
---
app/test/test_ethdev_api.c | 76 ++++++++++++++++++++++++++
doc/guides/rel_notes/release_26_07.rst | 6 ++
lib/ethdev/rte_flow.c | 46 ++++++++++++++--
lib/ethdev/rte_flow.h | 13 +++++
4 files changed, 135 insertions(+), 6 deletions(-)
diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c
index 76afd0345c..5cae1cdc1d 100644
--- a/app/test/test_ethdev_api.c
+++ b/app/test/test_ethdev_api.c
@@ -4,6 +4,7 @@
#include <rte_log.h>
#include <rte_ethdev.h>
+#include <rte_flow.h>
#include <rte_test.h>
#include "test.h"
@@ -15,6 +16,80 @@
#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 struct rte_flow_item pattern[] = {
+ {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .spec = &spec,
+ .last = &last,
+ .mask = &mask,
+ },
+ { .type = RTE_FLOW_ITEM_TYPE_END },
+ };
+ union {
+ struct rte_flow_item item;
+ struct rte_flow_item_eth eth;
+ double align;
+ uint8_t raw[256];
+ } dst;
+ const struct rte_flow_item *item;
+ const struct rte_flow_item_eth *conv_spec;
+ const struct rte_flow_item_eth *conv_last;
+ 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.raw),
+ "Masked pattern conversion needs too much storage");
+
+ memset(&dst, 0, sizeof(dst));
+ ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, dst.raw,
+ sizeof(dst.raw), pattern, NULL);
+ TEST_ASSERT(ret > 0, "Masked pattern conversion failed");
+
+ item = (const struct rte_flow_item *)dst.raw;
+ 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");
+
+ return TEST_SUCCESS;
+}
+
static int32_t
ethdev_api_queue_status(void)
{
@@ -167,6 +242,7 @@ static struct unit_test_suite ethdev_api_testsuite = {
.setup = NULL,
.teardown = NULL,
.unit_test_cases = {
+ TEST_CASE(ethdev_api_flow_conv_pattern_masked),
TEST_CASE(ethdev_api_queue_status),
/* TODO: Add deferred_start queue status test */
TEST_CASES_END() /**< NULL terminate unit test array */
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index b5285af5fe..4f5d21d576 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -190,6 +190,12 @@ API Changes
- ``rte_pmd_mlx5_enable_steering``
- ``rte_pmd_mlx5_disable_steering``
+* ethdev: Added masked pattern conversion.
+
+ Added ``RTE_FLOW_CONV_OP_PATTERN_MASKED`` to ``rte_flow_conv()``
+ to copy an entire pattern while applying each item's mask to its
+ ``spec`` and ``last`` fields.
+
ABI Changes
-----------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index ec0fe08355..c7a94a1194 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -178,6 +178,14 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)),
};
+static inline size_t
+rte_flow_conv_item_mask_size(const struct rte_flow_item *item)
+{
+ if ((int)item->type >= 0)
+ return rte_flow_desc_item[item->type].size;
+ return sizeof(void *);
+}
+
/** Generate flow_action[] entry. */
#define MK_FLOW_ACTION(t, s) \
[RTE_FLOW_ACTION_TYPE_ ## t] = { \
@@ -835,6 +843,8 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
* RTE_FLOW_ITEM_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
+ * @param[in] with_mask
+ * If true, @p src mask will be applied to spec and last.
*
* @return
* A positive value representing the number of bytes needed to store
@@ -847,12 +857,13 @@ 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;
size_t off;
size_t ret;
- unsigned int i;
+ unsigned int i, j;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
/**
@@ -876,15 +887,27 @@ 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 = src->mask;
+ size_t item_mask_size = mask ? rte_flow_conv_item_mask_size(src) : 0;
+
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 (with_mask && c_spec && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_spec[j] &= mask[j];
+ }
}
if (src->last) {
@@ -893,9 +916,17 @@ 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 (with_mask && c_last && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_last[j] &= mask[j];
+ }
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -1042,7 +1073,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)
@@ -1143,7 +1174,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;
@@ -1158,7 +1189,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);
@@ -1178,6 +1209,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
ret = rte_flow_conv_name(1, 1, 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 b495409406..959a2f903b 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4556,6 +4556,19 @@ enum rte_flow_conv_op {
* @code const char ** @endcode
*/
RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+
+ /**
+ * Convert an entire pattern.
+ *
+ * Duplicates all pattern items at once, applying @p mask to @p spec
+ * and @p last.
+ *
+ * - @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.34.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v5] ethdev: support inline calculating masked item value
2026-06-10 5:27 ` [PATCH v5] " Bing Zhao
@ 2026-06-10 15:46 ` Stephen Hemminger
2026-06-11 4:55 ` Bing Zhao
2026-10-07 16:57 ` [PATCH v6] " Bing Zhao
2 siblings, 0 replies; 24+ messages in thread
From: Stephen Hemminger @ 2026-06-10 15:46 UTC (permalink / raw)
To: Bing Zhao
Cc: viacheslavo, dev, rasland, orika, dsosnowski, suanmingm, matan,
thomas
On Wed, 10 Jun 2026 08:27:29 +0300
Bing Zhao <bingz@nvidia.com> wrote:
> In the asynchronous API definition and some drivers, the
> rte_flow_item spec value may not be calculated by the driver due to the
> reason of speed of light rule insertion rate and sometimes the input
> parameters will be copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword
> const and cannot be changed in the code itself. And also the driver
> needs some extra memory to do the calculation and extra conditions
> to understand the length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP
> was introduced to calculate the spec and last values after applying
> the mask inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
> ---
Detailed AI review still sees issues here:
On Wed, 10 Jun 2026 08:27:29 +0300, Bing Zhao wrote:
> Subject: [PATCH v5] ethdev: support inline calculating masked item value
> v5: handle some items separately and add test for them
The v5 lib/ethdev/rte_flow.c and app/test/test_ethdev_api.c hunks are
identical to v4 -- the masking loop is unchanged and the test still only
covers ETH. The changelog says items are handled separately and a test was
added, but no such change is present in the diff. The v4 issue is still open.
Error: byte-wise masking corrupts embedded pointers in deep-copy item
types (RAW, FLEX, GENEVE_OPT).
In rte_flow_conv_pattern(), the mask is applied over the fixed item struct:
size_t item_mask_size = mask ? rte_flow_conv_item_mask_size(src) : 0;
...
size_t mask_size = RTE_MIN(ret, item_mask_size);
for (j = 0; j < mask_size; j++)
c_spec[j] &= mask[j];
item_mask_size is rte_flow_desc_item[type].size, the fixed item struct size.
For RTE_FLOW_ITEM_TYPE_RAW, FLEX, and GENEVE_OPT, that fixed struct ends in an
embedded pointer that rte_flow_conv_item_spec() has just populated to point at
the deep-copied trailing data (rte_flow_item_raw.pattern,
rte_flow_item_flex.pattern, rte_flow_item_geneve_opt.data). Because the masked
range covers the whole fixed struct, the loop ANDs the bytes of that pointer
with the mask's corresponding bytes (typically a NULL mask pointer), zeroing
or garbling it.
The converted item's pattern/data pointer is clobbered while the copied
payload it should reference is left unreachable. A consumer that follows
conv->pattern then dereferences NULL or a corrupt address. Plain value items
(eth, ipv4, ...) are unaffected; only the deep-copy item types break.
Suggested fix: do not blind-mask the entire fixed struct for items that carry
an embedded pointer / desc_fn deep copy. Either skip masking when
rte_flow_desc_item[type].desc_fn != NULL, or mask only the leading plain-data
region and leave the pointer field (and trailing copied bytes) intact.
Warning: the new test validates only an ETH pattern, so the RAW/FLEX/GENEVE_OPT
path above is untested. A RAW item case would have surfaced the pointer
corruption -- and is what the v5 changelog claims to have added but did not.
Info: the Doxygen block for RTE_FLOW_CONV_OP_PATTERN_MASKED uses @p mask,
@p spec, @p last, but those are item fields, not parameters of the op; the
neighboring enum entries only document the @p src / @p dst types.
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH v5] ethdev: support inline calculating masked item value
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
2 siblings, 1 reply; 24+ messages in thread
From: Bing Zhao @ 2026-06-11 4:55 UTC (permalink / raw)
To: Bing Zhao, Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh,
stephen@networkplumber.org
Cc: Ori Kam, Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
Hi,
In my local code,
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7cf9f6f6f3..7a2721af00 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -181,9 +181,18 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
static inline size_t
rte_flow_conv_item_mask_size(const struct rte_flow_item *item)
{
- if ((int)item->type >= 0)
+ if ((int)item->type < 0)
+ return sizeof(void *);
+ 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, data);
+ default:
+ if (rte_flow_desc_item[item->type].desc_fn != NULL)
+ return 0;
return rte_flow_desc_item[item->type].size;
- return sizeof(void *);
+ }
}
// This is the code before my latest change.
> +static inline size_t
> +rte_flow_conv_item_mask_size(const struct rte_flow_item *item) {
> + if ((int)item->type >= 0)
> + return rte_flow_desc_item[item->type].size;
> + return sizeof(void *);
> +}
> +
I didn't understand why the patch I sent is still using the old code.
> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Wednesday, June 10, 2026 1:27 PM
> To: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> Darawsheh <rasland@nvidia.com>; stephen@networkplumber.org
> Cc: Ori Kam <orika@nvidia.com>; Dariusz Sosnowski <dsosnowski@nvidia.com>;
> Suanming Mou <suanmingm@nvidia.com>; Matan Azrad <matan@nvidia.com>; NBU-
> Contact-Thomas Monjalon (EXTERNAL) <thomas@monjalon.net>
> Subject: [PATCH v5] ethdev: support inline calculating masked item value
>
> External email: Use caution opening links or attachments
>
>
> In the asynchronous API definition and some drivers, the rte_flow_item
> spec value may not be calculated by the driver due to the reason of speed
> of light rule insertion rate and sometimes the input parameters will be
> copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword const
> and cannot be changed in the code itself. And also the driver needs some
> extra memory to do the calculation and extra conditions to understand the
> length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP was
> introduced to calculate the spec and last values after applying the mask
> inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
> ---
> 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
> ---
> app/test/test_ethdev_api.c | 76 ++++++++++++++++++++++++++
> doc/guides/rel_notes/release_26_07.rst | 6 ++
> lib/ethdev/rte_flow.c | 46 ++++++++++++++--
> lib/ethdev/rte_flow.h | 13 +++++
> 4 files changed, 135 insertions(+), 6 deletions(-)
>
> diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c index
> 76afd0345c..5cae1cdc1d 100644
> --- a/app/test/test_ethdev_api.c
> +++ b/app/test/test_ethdev_api.c
> @@ -4,6 +4,7 @@
>
> #include <rte_log.h>
> #include <rte_ethdev.h>
> +#include <rte_flow.h>
>
> #include <rte_test.h>
> #include "test.h"
> @@ -15,6 +16,80 @@
> #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 struct rte_flow_item pattern[] = {
> + {
> + .type = RTE_FLOW_ITEM_TYPE_ETH,
> + .spec = &spec,
> + .last = &last,
> + .mask = &mask,
> + },
> + { .type = RTE_FLOW_ITEM_TYPE_END },
> + };
> + union {
> + struct rte_flow_item item;
> + struct rte_flow_item_eth eth;
> + double align;
> + uint8_t raw[256];
> + } dst;
> + const struct rte_flow_item *item;
> + const struct rte_flow_item_eth *conv_spec;
> + const struct rte_flow_item_eth *conv_last;
> + 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.raw),
> + "Masked pattern conversion needs too much storage");
> +
> + memset(&dst, 0, sizeof(dst));
> + ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, dst.raw,
> + sizeof(dst.raw), pattern, NULL);
> + TEST_ASSERT(ret > 0, "Masked pattern conversion failed");
> +
> + item = (const struct rte_flow_item *)dst.raw;
> + 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");
> +
> + return TEST_SUCCESS;
> +}
> +
> static int32_t
> ethdev_api_queue_status(void)
> {
> @@ -167,6 +242,7 @@ static struct unit_test_suite ethdev_api_testsuite = {
> .setup = NULL,
> .teardown = NULL,
> .unit_test_cases = {
> + TEST_CASE(ethdev_api_flow_conv_pattern_masked),
> TEST_CASE(ethdev_api_queue_status),
> /* TODO: Add deferred_start queue status test */
> TEST_CASES_END() /**< NULL terminate unit test array */
> diff --git a/doc/guides/rel_notes/release_26_07.rst
> b/doc/guides/rel_notes/release_26_07.rst
> index b5285af5fe..4f5d21d576 100644
> --- a/doc/guides/rel_notes/release_26_07.rst
> +++ b/doc/guides/rel_notes/release_26_07.rst
> @@ -190,6 +190,12 @@ API Changes
> - ``rte_pmd_mlx5_enable_steering``
> - ``rte_pmd_mlx5_disable_steering``
>
> +* ethdev: Added masked pattern conversion.
> +
> + Added ``RTE_FLOW_CONV_OP_PATTERN_MASKED`` to ``rte_flow_conv()`` to
> + copy an entire pattern while applying each item's mask to its
> + ``spec`` and ``last`` fields.
> +
>
> ABI Changes
> -----------
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c index
> ec0fe08355..c7a94a1194 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -178,6 +178,14 @@ static const struct rte_flow_desc_data
> rte_flow_desc_item[] = {
> MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)), };
>
> +static inline size_t
> +rte_flow_conv_item_mask_size(const struct rte_flow_item *item) {
> + if ((int)item->type >= 0)
> + return rte_flow_desc_item[item->type].size;
> + return sizeof(void *);
> +}
> +
> /** Generate flow_action[] entry. */
> #define MK_FLOW_ACTION(t, s) \
> [RTE_FLOW_ACTION_TYPE_ ## t] = { \ @@ -835,6 +843,8 @@
> rte_flow_conv_action_conf(void *buf, const size_t size,
> * RTE_FLOW_ITEM_TYPE_END is encountered.
> * @param[out] error
> * Perform verbose error reporting if not NULL.
> + * @param[in] with_mask
> + * If true, @p src mask will be applied to spec and last.
> *
> * @return
> * A positive value representing the number of bytes needed to store
> @@ -847,12 +857,13 @@ 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;
> size_t off;
> size_t ret;
> - unsigned int i;
> + unsigned int i, j;
>
> for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
> /**
> @@ -876,15 +887,27 @@ 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 = src->mask;
> + size_t item_mask_size = mask ?
> + rte_flow_conv_item_mask_size(src) : 0;
> +
> 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 (with_mask && c_spec && mask) {
> + size_t mask_size = RTE_MIN(ret,
> + item_mask_size);
> +
> + for (j = 0; j < mask_size; j++)
> + c_spec[j] &= mask[j];
> + }
>
> }
> if (src->last) {
> @@ -893,9 +916,17 @@ 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 (with_mask && c_last && mask) {
> + size_t mask_size = RTE_MIN(ret,
> + item_mask_size);
> +
> + for (j = 0; j < mask_size; j++)
> + c_last[j] &= mask[j];
> + }
> }
> if (src->mask) {
> off = RTE_ALIGN_CEIL(off, sizeof(double)); @@ -
> 1042,7 +1073,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) @@ -1143,7 +1174,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;
> @@ -1158,7 +1189,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); @@
> -1178,6 +1209,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
> case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
> ret = rte_flow_conv_name(1, 1, 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
> b495409406..959a2f903b 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -4556,6 +4556,19 @@ enum rte_flow_conv_op {
> * @code const char ** @endcode
> */
> RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
> +
> + /**
> + * Convert an entire pattern.
> + *
> + * Duplicates all pattern items at once, applying @p mask to @p
> spec
> + * and @p last.
> + *
> + * - @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.34.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* RE: [PATCH v5] ethdev: support inline calculating masked item value
2026-06-11 4:55 ` Bing Zhao
@ 2026-06-11 4:56 ` Bing Zhao
0 siblings, 0 replies; 24+ messages in thread
From: Bing Zhao @ 2026-06-11 4:56 UTC (permalink / raw)
To: Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh,
stephen@networkplumber.org
Cc: Ori Kam, Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
Oh, I see, I squashed the changes into the next following mlx5 driver fix but not the proper one, so the code is still the old one. My fault.
> -----Original Message-----
> From: Bing Zhao
> Sent: Thursday, June 11, 2026 12:55 PM
> To: 'Bing Zhao' <bingz@nvidia.com>; Slava Ovsiienko
> <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan Darawsheh
> <rasland@nvidia.com>; stephen@networkplumber.org
> Cc: Ori Kam <orika@nvidia.com>; Dariusz Sosnowski <dsosnowski@nvidia.com>;
> Suanming Mou <suanmingm@nvidia.com>; Matan Azrad <matan@nvidia.com>; NBU-
> Contact-Thomas Monjalon (EXTERNAL) <thomas@monjalon.net>
> Subject: RE: [PATCH v5] ethdev: support inline calculating masked item
> value
>
> Hi,
>
> In my local code,
>
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c index
> 7cf9f6f6f3..7a2721af00 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -181,9 +181,18 @@ static const struct rte_flow_desc_data
> rte_flow_desc_item[] = { static inline size_t
> rte_flow_conv_item_mask_size(const struct rte_flow_item *item) {
> - if ((int)item->type >= 0)
> + if ((int)item->type < 0)
> + return sizeof(void *);
> + 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, data);
> + default:
> + if (rte_flow_desc_item[item->type].desc_fn != NULL)
> + return 0;
> return rte_flow_desc_item[item->type].size;
> - return sizeof(void *);
> + }
> }
>
> // This is the code before my latest change.
> > +static inline size_t
> > +rte_flow_conv_item_mask_size(const struct rte_flow_item *item) {
> > + if ((int)item->type >= 0)
> > + return rte_flow_desc_item[item->type].size;
> > + return sizeof(void *);
> > +}
> > +
>
>
> I didn't understand why the patch I sent is still using the old code.
>
> > -----Original Message-----
> > From: Bing Zhao <bingz@nvidia.com>
> > Sent: Wednesday, June 10, 2026 1:27 PM
> > To: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> > Darawsheh <rasland@nvidia.com>; stephen@networkplumber.org
> > Cc: Ori Kam <orika@nvidia.com>; Dariusz Sosnowski
> > <dsosnowski@nvidia.com>; Suanming Mou <suanmingm@nvidia.com>; Matan
> > Azrad <matan@nvidia.com>; NBU- Contact-Thomas Monjalon (EXTERNAL)
> > <thomas@monjalon.net>
> > Subject: [PATCH v5] ethdev: support inline calculating masked item
> > value
> >
> > External email: Use caution opening links or attachments
> >
> >
> > In the asynchronous API definition and some drivers, the rte_flow_item
> > spec value may not be calculated by the driver due to the reason of
> > speed of light rule insertion rate and sometimes the input parameters
> > will be copied and changed internally.
> >
> > After copying, the spec and last will be protected by the keyword
> > const and cannot be changed in the code itself. And also the driver
> > needs some extra memory to do the calculation and extra conditions to
> > understand the length of each item spec. This is not efficient.
> >
> > To solve the issue and support usage of the following fix, a new OP
> > was introduced to calculate the spec and last values after applying
> > the mask inline.
> >
> > Signed-off-by: Bing Zhao <bingz@nvidia.com>
> > Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
> > ---
> > 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
> > ---
> > app/test/test_ethdev_api.c | 76 ++++++++++++++++++++++++++
> > doc/guides/rel_notes/release_26_07.rst | 6 ++
> > lib/ethdev/rte_flow.c | 46 ++++++++++++++--
> > lib/ethdev/rte_flow.h | 13 +++++
> > 4 files changed, 135 insertions(+), 6 deletions(-)
> >
> > diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c
> > index 76afd0345c..5cae1cdc1d 100644
> > --- a/app/test/test_ethdev_api.c
> > +++ b/app/test/test_ethdev_api.c
> > @@ -4,6 +4,7 @@
> >
> > #include <rte_log.h>
> > #include <rte_ethdev.h>
> > +#include <rte_flow.h>
> >
> > #include <rte_test.h>
> > #include "test.h"
> > @@ -15,6 +16,80 @@
> > #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 struct rte_flow_item pattern[] = {
> > + {
> > + .type = RTE_FLOW_ITEM_TYPE_ETH,
> > + .spec = &spec,
> > + .last = &last,
> > + .mask = &mask,
> > + },
> > + { .type = RTE_FLOW_ITEM_TYPE_END },
> > + };
> > + union {
> > + struct rte_flow_item item;
> > + struct rte_flow_item_eth eth;
> > + double align;
> > + uint8_t raw[256];
> > + } dst;
> > + const struct rte_flow_item *item;
> > + const struct rte_flow_item_eth *conv_spec;
> > + const struct rte_flow_item_eth *conv_last;
> > + 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.raw),
> > + "Masked pattern conversion needs too much
> > + storage");
> > +
> > + memset(&dst, 0, sizeof(dst));
> > + ret = rte_flow_conv(RTE_FLOW_CONV_OP_PATTERN_MASKED, dst.raw,
> > + sizeof(dst.raw), pattern, NULL);
> > + TEST_ASSERT(ret > 0, "Masked pattern conversion failed");
> > +
> > + item = (const struct rte_flow_item *)dst.raw;
> > + 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");
> > +
> > + return TEST_SUCCESS;
> > +}
> > +
> > static int32_t
> > ethdev_api_queue_status(void)
> > {
> > @@ -167,6 +242,7 @@ static struct unit_test_suite ethdev_api_testsuite =
> {
> > .setup = NULL,
> > .teardown = NULL,
> > .unit_test_cases = {
> > + TEST_CASE(ethdev_api_flow_conv_pattern_masked),
> > TEST_CASE(ethdev_api_queue_status),
> > /* TODO: Add deferred_start queue status test */
> > TEST_CASES_END() /**< NULL terminate unit test array
> > */ diff --git a/doc/guides/rel_notes/release_26_07.rst
> > b/doc/guides/rel_notes/release_26_07.rst
> > index b5285af5fe..4f5d21d576 100644
> > --- a/doc/guides/rel_notes/release_26_07.rst
> > +++ b/doc/guides/rel_notes/release_26_07.rst
> > @@ -190,6 +190,12 @@ API Changes
> > - ``rte_pmd_mlx5_enable_steering``
> > - ``rte_pmd_mlx5_disable_steering``
> >
> > +* ethdev: Added masked pattern conversion.
> > +
> > + Added ``RTE_FLOW_CONV_OP_PATTERN_MASKED`` to ``rte_flow_conv()``
> > + to copy an entire pattern while applying each item's mask to its
> > + ``spec`` and ``last`` fields.
> > +
> >
> > ABI Changes
> > -----------
> > diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c index
> > ec0fe08355..c7a94a1194 100644
> > --- a/lib/ethdev/rte_flow.c
> > +++ b/lib/ethdev/rte_flow.c
> > @@ -178,6 +178,14 @@ static const struct rte_flow_desc_data
> > rte_flow_desc_item[] = {
> > MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)),
> > };
> >
> > +static inline size_t
> > +rte_flow_conv_item_mask_size(const struct rte_flow_item *item) {
> > + if ((int)item->type >= 0)
> > + return rte_flow_desc_item[item->type].size;
> > + return sizeof(void *);
> > +}
> > +
> > /** Generate flow_action[] entry. */
> > #define MK_FLOW_ACTION(t, s) \
> > [RTE_FLOW_ACTION_TYPE_ ## t] = { \ @@ -835,6 +843,8 @@
> > rte_flow_conv_action_conf(void *buf, const size_t size,
> > * RTE_FLOW_ITEM_TYPE_END is encountered.
> > * @param[out] error
> > * Perform verbose error reporting if not NULL.
> > + * @param[in] with_mask
> > + * If true, @p src mask will be applied to spec and last.
> > *
> > * @return
> > * A positive value representing the number of bytes needed to store
> > @@ -847,12 +857,13 @@ 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;
> > size_t off;
> > size_t ret;
> > - unsigned int i;
> > + unsigned int i, j;
> >
> > for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
> > /**
> > @@ -876,15 +887,27 @@ 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 = src->mask;
> > + size_t item_mask_size = mask ?
> > + rte_flow_conv_item_mask_size(src) : 0;
> > +
> > 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 (with_mask && c_spec && mask) {
> > + size_t mask_size = RTE_MIN(ret,
> > + item_mask_size);
> > +
> > + for (j = 0; j < mask_size; j++)
> > + c_spec[j] &= mask[j];
> > + }
> >
> > }
> > if (src->last) {
> > @@ -893,9 +916,17 @@ 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 (with_mask && c_last && mask) {
> > + size_t mask_size = RTE_MIN(ret,
> > + item_mask_size);
> > +
> > + for (j = 0; j < mask_size; j++)
> > + c_last[j] &= mask[j];
> > + }
> > }
> > if (src->mask) {
> > off = RTE_ALIGN_CEIL(off, sizeof(double)); @@
> > -
> > 1042,7 +1073,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) @@ -1143,7
> > +1174,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;
> > @@ -1158,7 +1189,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);
> > @@
> > -1178,6 +1209,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
> > case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
> > ret = rte_flow_conv_name(1, 1, 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
> > b495409406..959a2f903b 100644
> > --- a/lib/ethdev/rte_flow.h
> > +++ b/lib/ethdev/rte_flow.h
> > @@ -4556,6 +4556,19 @@ enum rte_flow_conv_op {
> > * @code const char ** @endcode
> > */
> > RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
> > +
> > + /**
> > + * Convert an entire pattern.
> > + *
> > + * Duplicates all pattern items at once, applying @p mask to
> > + @p
> > spec
> > + * and @p last.
> > + *
> > + * - @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.34.1
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v6] ethdev: support inline calculating masked item value
2026-06-10 5:27 ` [PATCH v5] " Bing Zhao
2026-06-10 15:46 ` Stephen Hemminger
2026-06-11 4:55 ` Bing Zhao
@ 2026-10-07 16:57 ` Bing Zhao
2026-10-08 7:17 ` Bing Zhao
` (2 more replies)
2 siblings, 3 replies; 24+ messages in thread
From: Bing Zhao @ 2026-10-07 16:57 UTC (permalink / raw)
To: viacheslavo, dev, rasland, stephen
Cc: orika, dsosnowski, suanmingm, matan, thomas
In the asynchronous API definition and some drivers, the
rte_flow_item spec value may not be calculated by the driver due to the
reason of speed of light rule insertion rate and sometimes the input
parameters will be copied and changed internally.
After copying, the spec and last will be protected by the keyword
const and cannot be changed in the code itself. And also the driver
needs some extra memory to do the calculation and extra conditions
to understand the length of each item spec. This is not efficient.
To solve the issue and support usage of the following fix, a new OP
was introduced to calculate the spec and last values after applying
the mask inline.
Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
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
---
app/test/test_ethdev_api.c | 123 +++++++++++++++++++++++++++++++++++++
lib/ethdev/rte_flow.c | 55 +++++++++++++++--
lib/ethdev/rte_flow.h | 13 ++++
3 files changed, 185 insertions(+), 6 deletions(-)
diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c
index 00d6a5c614..ba3e785425 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,125 @@
#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 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 = NULL,
+ };
+ 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),
+ "Converted RAW pattern mismatch");
+
+ return TEST_SUCCESS;
+}
+
static int32_t
ethdev_api_queue_status(void)
{
@@ -167,6 +289,7 @@ static struct unit_test_suite ethdev_api_testsuite = {
.setup = NULL,
.teardown = NULL,
.unit_test_cases = {
+ TEST_CASE(ethdev_api_flow_conv_pattern_masked),
TEST_CASE(ethdev_api_queue_status),
/* TODO: Add deferred_start queue status test */
TEST_CASES_END() /**< NULL terminate unit test array */
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index ca2f85c3fa..e4efcbf10f 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -175,6 +175,23 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)),
};
+static inline size_t
+rte_flow_conv_item_mask_size(const struct rte_flow_item *item)
+{
+ if ((int)item->type < 0)
+ return sizeof(void *);
+ 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, data);
+ 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] = { \
@@ -797,6 +814,8 @@ rte_flow_conv_action_conf(void *buf, const size_t size,
* RTE_FLOW_ITEM_TYPE_END is encountered.
* @param[out] error
* Perform verbose error reporting if not NULL.
+ * @param[in] with_mask
+ * If true, @p src mask will be applied to spec and last.
*
* @return
* A positive value representing the number of bytes needed to store
@@ -809,12 +828,13 @@ 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;
size_t off;
size_t ret;
- unsigned int i;
+ unsigned int i, j;
for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
/**
@@ -838,15 +858,27 @@ 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 = src->mask;
+ size_t item_mask_size = mask ? rte_flow_conv_item_mask_size(src) : 0;
+
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 (with_mask && c_spec && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_spec[j] &= mask[j];
+ }
}
if (src->last) {
@@ -855,9 +887,17 @@ 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 (with_mask && c_last && mask) {
+ size_t mask_size = RTE_MIN(ret, item_mask_size);
+
+ for (j = 0; j < mask_size; j++)
+ c_last[j] &= mask[j];
+ }
}
if (src->mask) {
off = RTE_ALIGN_CEIL(off, sizeof(double));
@@ -1004,7 +1044,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)
@@ -1104,7 +1144,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;
@@ -1119,7 +1159,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);
@@ -1139,6 +1179,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
ret = rte_flow_conv_name(1, 1, 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 f864578f80..2261d1ea06 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4544,6 +4544,19 @@ enum rte_flow_conv_op {
* @code const char ** @endcode
*/
RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
+
+ /**
+ * Convert an entire pattern.
+ *
+ * Duplicates all pattern items at once, applying each source item mask
+ * to its copied specification and range.
+ *
+ * - @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
^ permalink raw reply related [flat|nested] 24+ messages in thread
* RE: [PATCH v6] ethdev: support inline calculating masked item value
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
2 siblings, 0 replies; 24+ messages in thread
From: Bing Zhao @ 2026-10-08 7:17 UTC (permalink / raw)
To: Bing Zhao, Slava Ovsiienko, dev@dpdk.org, Raslan Darawsheh,
stephen@networkplumber.org
Cc: Ori Kam, Dariusz Sosnowski, Suanming Mou, Matan Azrad,
NBU-Contact-Thomas Monjalon (EXTERNAL)
No. The three errors in that AI review are not valid, and the warnings do not block this patch.
rte_flow_conv_item_spec() returns size_t, the number of bytes to copy. It does not return a negative errno. In rte_flow_conv_pattern() the local ret is also size_t, so if (ret < 0) is always false and cannot be the missing check the review describes. That copy pattern was already there for spec, last, and mask. This patch only ANDs the mask onto a buffer after that copy has produced a pointer.
The other notes are process nits:
The with_mask comment is already clear.
A release note belongs in the current release file, not release_25_03.rst. This master has no current notes file, and release_26_07.rst is gone, which is why that hunk was dropped.
RTE_FLOW_CONV_OP_PATTERN_MASKED is one new enumerator at the end of an existing enum. It cannot be tagged __rte_experimental, and existing callers of rte_flow_conv() are unchanged.
https://mails.dpdk.org/archives/test-report/2026-October/1057123.html
@stephen@networkplumber.org, FYI
> -----Original Message-----
> From: Bing Zhao <bingz@nvidia.com>
> Sent: Thursday, October 8, 2026 12:58 AM
> To: Slava Ovsiienko <viacheslavo@nvidia.com>; dev@dpdk.org; Raslan
> Darawsheh <rasland@nvidia.com>; stephen@networkplumber.org
> Cc: Ori Kam <orika@nvidia.com>; Dariusz Sosnowski <dsosnowski@nvidia.com>;
> Suanming Mou <suanmingm@nvidia.com>; Matan Azrad <matan@nvidia.com>; NBU-
> Contact-Thomas Monjalon (EXTERNAL) <thomas@monjalon.net>
> Subject: [PATCH v6] ethdev: support inline calculating masked item value
>
> External email: Use caution opening links or attachments
>
>
> In the asynchronous API definition and some drivers, the rte_flow_item
> spec value may not be calculated by the driver due to the reason of speed
> of light rule insertion rate and sometimes the input parameters will be
> copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword const
> and cannot be changed in the code itself. And also the driver needs some
> extra memory to do the calculation and extra conditions to understand the
> length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP was
> introduced to calculate the spec and last values after applying the mask
> inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
> ---
> 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
> ---
> app/test/test_ethdev_api.c | 123 +++++++++++++++++++++++++++++++++++++
> lib/ethdev/rte_flow.c | 55 +++++++++++++++--
> lib/ethdev/rte_flow.h | 13 ++++
> 3 files changed, 185 insertions(+), 6 deletions(-)
>
> diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c index
> 00d6a5c614..ba3e785425 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,125 @@
> #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 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 = NULL,
> + };
> + 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),
> + "Converted RAW pattern mismatch");
> +
> + return TEST_SUCCESS;
> +}
> +
> static int32_t
> ethdev_api_queue_status(void)
> {
> @@ -167,6 +289,7 @@ static struct unit_test_suite ethdev_api_testsuite = {
> .setup = NULL,
> .teardown = NULL,
> .unit_test_cases = {
> + TEST_CASE(ethdev_api_flow_conv_pattern_masked),
> TEST_CASE(ethdev_api_queue_status),
> /* TODO: Add deferred_start queue status test */
> TEST_CASES_END() /**< NULL terminate unit test array */
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c index
> ca2f85c3fa..e4efcbf10f 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -175,6 +175,23 @@ static const struct rte_flow_desc_data
> rte_flow_desc_item[] = {
> MK_FLOW_ITEM(COMPARE, sizeof(struct rte_flow_item_compare)), };
>
> +static inline size_t
> +rte_flow_conv_item_mask_size(const struct rte_flow_item *item) {
> + if ((int)item->type < 0)
> + return sizeof(void *);
> + 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, data);
> + 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] = { \ @@ -797,6 +814,8 @@
> rte_flow_conv_action_conf(void *buf, const size_t size,
> * RTE_FLOW_ITEM_TYPE_END is encountered.
> * @param[out] error
> * Perform verbose error reporting if not NULL.
> + * @param[in] with_mask
> + * If true, @p src mask will be applied to spec and last.
> *
> * @return
> * A positive value representing the number of bytes needed to store
> @@ -809,12 +828,13 @@ 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;
> size_t off;
> size_t ret;
> - unsigned int i;
> + unsigned int i, j;
>
> for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
> /**
> @@ -838,15 +858,27 @@ 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 = src->mask;
> + size_t item_mask_size = mask ?
> + rte_flow_conv_item_mask_size(src) : 0;
> +
> 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 (with_mask && c_spec && mask) {
> + size_t mask_size = RTE_MIN(ret,
> + item_mask_size);
> +
> + for (j = 0; j < mask_size; j++)
> + c_spec[j] &= mask[j];
> + }
>
> }
> if (src->last) {
> @@ -855,9 +887,17 @@ 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 (with_mask && c_last && mask) {
> + size_t mask_size = RTE_MIN(ret,
> + item_mask_size);
> +
> + for (j = 0; j < mask_size; j++)
> + c_last[j] &= mask[j];
> + }
> }
> if (src->mask) {
> off = RTE_ALIGN_CEIL(off, sizeof(double)); @@ -
> 1004,7 +1044,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) @@ -1104,7 +1144,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;
> @@ -1119,7 +1159,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); @@
> -1139,6 +1179,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
> case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
> ret = rte_flow_conv_name(1, 1, 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
> f864578f80..2261d1ea06 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -4544,6 +4544,19 @@ enum rte_flow_conv_op {
> * @code const char ** @endcode
> */
> RTE_FLOW_CONV_OP_ACTION_NAME_PTR,
> +
> + /**
> + * Convert an entire pattern.
> + *
> + * Duplicates all pattern items at once, applying each source item
> mask
> + * to its copied specification and range.
> + *
> + * - @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
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6] ethdev: support inline calculating masked item value
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
2 siblings, 0 replies; 24+ messages in thread
From: Stephen Hemminger @ 2026-10-08 15:27 UTC (permalink / raw)
To: Bing Zhao
Cc: viacheslavo, dev, rasland, orika, dsosnowski, suanmingm, matan,
thomas
On Wed, 7 Oct 2026 19:57:44 +0300
Bing Zhao <bingz@nvidia.com> wrote:
> In the asynchronous API definition and some drivers, the
> rte_flow_item spec value may not be calculated by the driver due to the
> reason of speed of light rule insertion rate and sometimes the input
> parameters will be copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword
> const and cannot be changed in the code itself. And also the driver
> needs some extra memory to do the calculation and extra conditions
> to understand the length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP
> was introduced to calculate the spec and last values after applying
> the mask inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
> ---
Existing AI review done by CI is useless at this point...
Running a more detailed review does find some things.
Review: [PATCH v6] ethdev: support inline calculating masked item value
Error
-----
1. NULL mask leaves spec/last unmasked.
rte_flow semantics: a NULL item mask means the default mask
(rte_flow_item_<name>_mask) applies, not "match all bits". Here
item_mask_size is 0 when src->mask is NULL, so spec and last are
copied raw. For ETH the default mask is all ones so nothing shows,
but for IPV4 (default mask covers only src/dst addr) tos, ttl,
etc. come back unmasked. A driver relying on the new op to get
"masked values" gets wrong ones.
rte_flow.c has no type -> default mask table, so either add one
to rte_flow_desc_data, or document that a NULL mask is not
applied and the caller must handle it (see doc text in Warning 3).
Warning
-------
1. PMD private items get their opaque data ANDed.
rte_flow_conv_item_mask_size() returns sizeof(void *) for
(int)type < 0, so the first pointer-size bytes of a private spec
are ANDed with the first bytes of its mask. ethdev has no idea
what those bytes mean. Leave them alone:
if ((int)item->type < 0)
return 0;
2. Variable part of RAW and GENEVE_OPT is not masked.
item_mask_size stops at offsetof(pattern) / offsetof(data), so
the copied pattern bytes and option data are never ANDed with
mask->pattern / mask->data. The same holds for FLEX (desc_fn
returns 0). Either mask them or document it; the current comment
says the mask is applied to the spec, which is not true for these.
3. Doxygen for RTE_FLOW_CONV_OP_PATTERN_MASKED is a copy of
OP_PATTERN and does not describe the limits above. Suggest:
/**
* 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;
* - FLEX and PMD private items.
*
* - @p src type:
* @code const struct rte_flow_item * @endcode
* - @p dst type:
* @code struct rte_flow_item * @endcode
*/
4. Missing release notes. New rte_flow_conv() op is a new API and
needs an entry in doc/guides/rel_notes/release_26_11.rst.
5. Commit message is hard to parse ("due to the reason of speed of
light rule insertion rate", "support usage of the following fix"
refers to a patch not in this series). Suggested body:
Add RTE_FLOW_CONV_OP_PATTERN_MASKED to rte_flow_conv(). It
copies a pattern like RTE_FLOW_CONV_OP_PATTERN and ANDs each
item spec and last with the item mask.
Drivers using the async flow API copy patterns on the rule
insertion path and need masked values. The copied spec and
last are const, so masking them in the driver needs extra
memory and per-item length handling. Doing it during the
conversion avoids both.
Info
----
1. No in-tree user. Please send the mlx5 consumer in the same series
so the API is exercised and reviewed in context.
2. Spec and last masking loops are duplicated, and item_mask_size is
computed even when with_mask is false. Factor out:
static 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];
}
and compute the size only under with_mask && mask.
3. Test does not cover a NULL-mask item or an item with a non-trivial
default mask (e.g. IPV4). Add one once Error 1 is settled so the
chosen behaviour is locked in.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v7] ethdev: support inline calculating masked item value
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 ` Bing Zhao
2026-10-13 15:45 ` Stephen Hemminger
2 siblings, 1 reply; 24+ messages in thread
From: Bing Zhao @ 2026-10-13 13:49 UTC (permalink / raw)
To: viacheslavo, dev, rasland, stephen
Cc: orika, dsosnowski, suanmingm, matan, thomas
In the asynchronous API definition and some drivers, the
rte_flow_item spec value may not be calculated by the driver due to the
reason of speed of light rule insertion rate and sometimes the input
parameters will be copied and changed internally.
After copying, the spec and last will be protected by the keyword
const and cannot be changed in the code itself. And also the driver
needs some extra memory to do the calculation and extra conditions
to understand the length of each item spec. This is not efficient.
To solve the issue and support usage of the following fix, a new OP
was introduced to calculate the spec and last values after applying
the mask inline.
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
---
app/test/test_ethdev_api.c | 338 +++++++++++++++++++++++++
doc/guides/rel_notes/release_26_11.rst | 6 +
lib/ethdev/rte_flow.c | 76 +++++-
lib/ethdev/rte_flow.h | 16 ++
4 files changed, 430 insertions(+), 6 deletions(-)
diff --git a/app/test/test_ethdev_api.c b/app/test/test_ethdev_api.c
index 76afd0345c..ef4c318b7d 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,323 @@
#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 data and COMPARE
+ * items are not.
+ */
+ {
+ 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 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,
+ .mask = &opt_mask,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_COMPARE,
+ .spec = &cmp_spec,
+ .mask = &cmp_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;
+
+ 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, 0,
+ "GENEVE_OPT length must 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");
+
+ 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");
+ }
+
+ return TEST_SUCCESS;
+}
+
static int32_t
ethdev_api_queue_status(void)
{
@@ -162,6 +482,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 a6df347411..31c909dad9 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -82,6 +82,12 @@ New Features
Added support for the VFIO character device (cdev) API, also known as IOMMUFD.
DPDK now supports both the group mode and the new cdev mode.
+* **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 each item ``spec`` and ``last`` with the item mask.
+
* **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 1056cfd2c5..f74b5353a0 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -179,6 +179,34 @@ 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 and GENEVE_OPT stop before
+ * that pointer, so the payload it refers to 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, data);
+ 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] = { \
@@ -821,6 +849,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.
*
@@ -834,6 +872,10 @@ 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 data, FLEX, COMPARE and PMD private items are not masked.
* @param[out] error
* Perform verbose error reporting if not NULL.
*
@@ -848,6 +890,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;
@@ -877,16 +920,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));
@@ -894,9 +950,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));
@@ -1043,7 +1104,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)
@@ -1144,7 +1205,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;
@@ -1159,7 +1220,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);
@@ -1179,6 +1240,9 @@ rte_flow_conv(enum rte_flow_conv_op op,
case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
ret = rte_flow_conv_name(1, 1, 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 1bfe1b3f61..428e5f4fd4 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4411,6 +4411,22 @@ enum rte_flow_conv_op {
* @code const char ** @endcode
*/
RTE_FLOW_CONV_OP_ACTION_NAME_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;
+ * - 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
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v7] ethdev: support inline calculating masked item value
2026-10-13 13:49 ` [PATCH v7] " Bing Zhao
@ 2026-10-13 15:45 ` Stephen Hemminger
0 siblings, 0 replies; 24+ messages in thread
From: Stephen Hemminger @ 2026-10-13 15:45 UTC (permalink / raw)
To: Bing Zhao
Cc: viacheslavo, dev, rasland, orika, dsosnowski, suanmingm, matan,
thomas
On Tue, 13 Oct 2026 16:49:50 +0300
Bing Zhao <bingz@nvidia.com> wrote:
> In the asynchronous API definition and some drivers, the
> rte_flow_item spec value may not be calculated by the driver due to the
> reason of speed of light rule insertion rate and sometimes the input
> parameters will be copied and changed internally.
>
> After copying, the spec and last will be protected by the keyword
> const and cannot be changed in the code itself. And also the driver
> needs some extra memory to do the calculation and extra conditions
> to understand the length of each item spec. This is not efficient.
>
> To solve the issue and support usage of the following fix, a new OP
> was introduced to calculate the spec and last values after applying
> the mask inline.
>
> Signed-off-by: Bing Zhao <bingz@nvidia.com>
> Reviewed-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
> ---
Semi-automated AI review had some feedback.
I assume mlx5 will come as followon so complaints about just this patch
are not really valid. And not concerned about its complaints
about going over flow twice when copying.
[PATCH v7] ethdev: support inline calculating masked item value
Applies to main, builds with -Dwerror=true.
Warning
1. GENEVE_OPT option_len is masked, data length is not.
rte_flow_conv_item_mask_size() returns offsetof(..., data), so
option_len is ANDed with the mask. The default
rte_flow_item_geneve_opt_mask has option_len = 0, while the deep
copy of data is sized from the unmasked spec->option_len. The output
is a spec with option_len == 0 pointing at option_len * 4 bytes of
data; the new test asserts exactly that. mlx5 sizes the option data
from spec->option_len (mlx5_flow.c, data_len = spec->option_len * 4),
so a driver switched to this op loses the option length.
option_len is a length, not a match value. Leave it unmasked:
case RTE_FLOW_ITEM_TYPE_GENEVE_OPT:
return offsetof(struct rte_flow_item_geneve_opt,
option_len);
and change the test to assert option_len is preserved.
2. TAG index is a selector, same reasoning as COMPARE.
rte_flow_item_tag.index picks the register, it is not matched
against packet data. mlx5 takes it from the unmasked spec on purpose
(mlx5_flow_dv.c: "When set mask, the index should be from spec").
With a template mask that has index == 0, this op returns index 0,
i.e. a different tag. Exclude it:
case RTE_FLOW_ITEM_TYPE_TAG:
return offsetof(struct rte_flow_item_tag, index);
Update the "Not masked" list in rte_flow.h, the comment above
rte_flow_conv_item_mask_size() and the rte_flow_conv_pattern()
doxygen to match, and add a TAG case to the test.
3. No user of the new op.
The commit message refers to "the following fix", but nothing in
this submission or in tree uses RTE_FLOW_CONV_OP_PATTERN_MASKED.
Send it as a series with the driver change that consumes it, so the
semantics above can be checked against a real caller.
4. Commit message needs rewriting. Suggested body:
Drivers translating async flow rules get const spec/last and must
copy each item to apply the mask, which needs per-item knowledge
of the spec size.
Add RTE_FLOW_CONV_OP_PATTERN_MASKED to rte_flow_conv(). It copies
the pattern like RTE_FLOW_CONV_OP_PATTERN and ANDs each copied
spec and last with the item mask. Pointed-to data (RAW pattern,
GENEVE_OPT data), selector fields, FLEX, COMPARE and PMD private
items are not masked.
Info
5. RAW pattern bytes are deep-copied into the writable output buffer
and spec.length is already masked, so masking the copied pattern
with mask->pattern for that length is straightforward. If it is left
out on purpose, say why in the commit message.
6. The insertion-rate argument needs numbers. rte_flow_conv() walks
the pattern twice (validate, copy) and masks byte by byte; show it
is not slower than the driver-local copy it replaces, or drop the
claim.
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2026-10-13 15:45 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).