[v5,03/10] ethdev: bring in async queue-based flow rules operations

Message ID 20220211022653.1372318-4-akozyrev@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series ethdev: datapath-focused flow rules management |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Alexander Kozyrev Feb. 11, 2022, 2:26 a.m. UTC
  A new, faster, queue-based flow rules management mechanism is needed for
applications offloading rules inside the datapath. This asynchronous
and lockless mechanism frees the CPU for further packet processing and
reduces the performance impact of the flow rules creation/destruction
on the datapath. Note that queues are not thread-safe and the queue
should be accessed from the same thread for all queue operations.
It is the responsibility of the app to sync the queue functions in case
of multi-threaded access to the same queue.

The rte_flow_q_flow_create() function enqueues a flow creation to the
requested queue. It benefits from already configured resources and sets
unique values on top of item and action templates. A flow rule is enqueued
on the specified flow queue and offloaded asynchronously to the hardware.
The function returns immediately to spare CPU for further packet
processing. The application must invoke the rte_flow_q_pull() function
to complete the flow rule operation offloading, to clear the queue, and to
receive the operation status. The rte_flow_q_flow_destroy() function
enqueues a flow destruction to the requested queue.

Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
 doc/guides/prog_guide/img/rte_flow_q_init.svg | 205 ++++++++++
 .../prog_guide/img/rte_flow_q_usage.svg       | 351 ++++++++++++++++++
 doc/guides/prog_guide/rte_flow.rst            | 167 ++++++++-
 doc/guides/rel_notes/release_22_03.rst        |   8 +
 lib/ethdev/rte_flow.c                         | 175 ++++++++-
 lib/ethdev/rte_flow.h                         | 334 +++++++++++++++++
 lib/ethdev/rte_flow_driver.h                  |  55 +++
 lib/ethdev/version.map                        |   7 +
 8 files changed, 1300 insertions(+), 2 deletions(-)
 create mode 100644 doc/guides/prog_guide/img/rte_flow_q_init.svg
 create mode 100644 doc/guides/prog_guide/img/rte_flow_q_usage.svg
  

Comments

Andrew Rybchenko Feb. 11, 2022, 12:42 p.m. UTC | #1
On 2/11/22 05:26, Alexander Kozyrev wrote:
> A new, faster, queue-based flow rules management mechanism is needed for
> applications offloading rules inside the datapath. This asynchronous
> and lockless mechanism frees the CPU for further packet processing and
> reduces the performance impact of the flow rules creation/destruction
> on the datapath. Note that queues are not thread-safe and the queue
> should be accessed from the same thread for all queue operations.
> It is the responsibility of the app to sync the queue functions in case
> of multi-threaded access to the same queue.
> 
> The rte_flow_q_flow_create() function enqueues a flow creation to the
> requested queue. It benefits from already configured resources and sets
> unique values on top of item and action templates. A flow rule is enqueued
> on the specified flow queue and offloaded asynchronously to the hardware.
> The function returns immediately to spare CPU for further packet
> processing. The application must invoke the rte_flow_q_pull() function
> to complete the flow rule operation offloading, to clear the queue, and to
> receive the operation status. The rte_flow_q_flow_destroy() function
> enqueues a flow destruction to the requested queue.
> 
> Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>
> ---
>   doc/guides/prog_guide/img/rte_flow_q_init.svg | 205 ++++++++++
>   .../prog_guide/img/rte_flow_q_usage.svg       | 351 ++++++++++++++++++
>   doc/guides/prog_guide/rte_flow.rst            | 167 ++++++++-
>   doc/guides/rel_notes/release_22_03.rst        |   8 +
>   lib/ethdev/rte_flow.c                         | 175 ++++++++-
>   lib/ethdev/rte_flow.h                         | 334 +++++++++++++++++
>   lib/ethdev/rte_flow_driver.h                  |  55 +++
>   lib/ethdev/version.map                        |   7 +
>   8 files changed, 1300 insertions(+), 2 deletions(-)
>   create mode 100644 doc/guides/prog_guide/img/rte_flow_q_init.svg
>   create mode 100644 doc/guides/prog_guide/img/rte_flow_q_usage.svg
> 
> diff --git a/doc/guides/prog_guide/img/rte_flow_q_init.svg b/doc/guides/prog_guide/img/rte_flow_q_init.svg
> new file mode 100644
> index 0000000000..96160bde42
> --- /dev/null
> +++ b/doc/guides/prog_guide/img/rte_flow_q_init.svg
> @@ -0,0 +1,205 @@
> +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> +<!-- SPDX-License-Identifier: BSD-3-Clause -->
> +
> +<!-- Copyright(c) 2022 NVIDIA Corporation & Affiliates -->
> +
> +<svg
> +   width="485"
> +   height="535"
> +   overflow="hidden"
> +   version="1.1"
> +   id="svg61"
> +   sodipodi:docname="rte_flow_q_init.svg"
> +   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
> +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
> +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
> +   xmlns="http://www.w3.org/2000/svg"
> +   xmlns:svg="http://www.w3.org/2000/svg">
> +  <sodipodi:namedview
> +     id="namedview63"
> +     pagecolor="#ffffff"
> +     bordercolor="#666666"
> +     borderopacity="1.0"
> +     inkscape:pageshadow="2"
> +     inkscape:pageopacity="0.0"
> +     inkscape:pagecheckerboard="0"
> +     showgrid="false"
> +     inkscape:zoom="1.517757"
> +     inkscape:cx="242.79249"
> +     inkscape:cy="267.17057"
> +     inkscape:window-width="2400"
> +     inkscape:window-height="1271"
> +     inkscape:window-x="2391"
> +     inkscape:window-y="-9"
> +     inkscape:window-maximized="1"
> +     inkscape:current-layer="g59" />
> +  <defs
> +     id="defs5">
> +    <clipPath
> +       id="clip0">
> +      <rect
> +         x="0"
> +         y="0"
> +         width="485"
> +         height="535"
> +         id="rect2" />
> +    </clipPath>
> +  </defs>
> +  <g
> +     clip-path="url(#clip0)"
> +     id="g59">
> +    <rect
> +       x="0"
> +       y="0"
> +       width="485"
> +       height="535"
> +       fill="#FFFFFF"
> +       id="rect7" />
> +    <rect
> +       x="0.500053"
> +       y="79.5001"
> +       width="482"
> +       height="59"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#A6A6A6"
> +       id="rect9" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="24"
> +       transform="translate(121.6 116)"
> +       id="text13">
> +         rte_eth_dev_configure
> +         <tspan
> +   font-size="24"
> +   x="224.007"
> +   y="0"
> +   id="tspan11">()</tspan></text>
> +    <rect
> +       x="0.500053"
> +       y="158.5"
> +       width="482"
> +       height="59"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect15" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="24"
> +       transform="translate(140.273 195)"
> +       id="text17">
> +         rte_flow_configure()
> +      </text>
> +    <rect
> +       x="0.500053"
> +       y="236.5"
> +       width="482"
> +       height="60"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect19" />
> +    <text
> +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> +       font-weight="400"
> +       font-size="24px"
> +       id="text21"
> +       x="63.425903"
> +       y="274">rte_flow_pattern_template_create()</text>
> +    <rect
> +       x="0.500053"
> +       y="316.5"
> +       width="482"
> +       height="59"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect23" />
> +    <text
> +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> +       font-weight="400"
> +       font-size="24px"
> +       id="text27"
> +       x="69.379204"
> +       y="353">rte_flow_actions_template_create()</text>
> +    <rect
> +       x="0.500053"
> +       y="0.500053"
> +       width="482"
> +       height="60"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#A6A6A6"
> +       id="rect29" />
> +    <text
> +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> +       font-weight="400"
> +       font-size="24px"
> +       transform="translate(177.233,37)"
> +       id="text33">rte_eal_init()</text>
> +    <path
> +       d="M2-1.09108e-05 2.00005 9.2445-1.99995 9.24452-2 1.09108e-05ZM6.00004 7.24448 0.000104987 19.2445-5.99996 7.24455Z"
> +       transform="matrix(-1 0 0 1 241 60)"
> +       id="path35" />
> +    <path
> +       d="M2-1.08133e-05 2.00005 9.41805-1.99995 9.41807-2 1.08133e-05ZM6.00004 7.41802 0.000104987 19.4181-5.99996 7.41809Z"
> +       transform="matrix(-1 0 0 1 241 138)"
> +       id="path37" />
> +    <path
> +       d="M2-1.09108e-05 2.00005 9.2445-1.99995 9.24452-2 1.09108e-05ZM6.00004 7.24448 0.000104987 19.2445-5.99996 7.24455Z"
> +       transform="matrix(-1 0 0 1 241 217)"
> +       id="path39" />
> +    <rect
> +       x="0.500053"
> +       y="395.5"
> +       width="482"
> +       height="59"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect41" />
> +    <text
> +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> +       font-weight="400"
> +       font-size="24px"
> +       id="text47"
> +       x="76.988998"
> +       y="432">rte_flow_template_table_create()</text>
> +    <path
> +       d="M2-1.05859e-05 2.00005 9.83526-1.99995 9.83529-2 1.05859e-05ZM6.00004 7.83524 0.000104987 19.8353-5.99996 7.83531Z"
> +       transform="matrix(-1 0 0 1 241 296)"
> +       id="path49" />
> +    <path
> +       d="M243 375 243 384.191 239 384.191 239 375ZM247 382.191 241 394.191 235 382.191Z"
> +       id="path51" />
> +    <rect
> +       x="0.500053"
> +       y="473.5"
> +       width="482"
> +       height="60"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#A6A6A6"
> +       id="rect53" />
> +    <text
> +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> +       font-weight="400"
> +       font-size="24px"
> +       id="text55"
> +       x="149.30299"
> +       y="511">rte_eth_dev_start()</text>
> +    <path
> +       d="M245 454 245 463.191 241 463.191 241 454ZM249 461.191 243 473.191 237 461.191Z"
> +       id="path57" />
> +  </g>
> +</svg>
> diff --git a/doc/guides/prog_guide/img/rte_flow_q_usage.svg b/doc/guides/prog_guide/img/rte_flow_q_usage.svg
> new file mode 100644
> index 0000000000..a1f6c0a0a8
> --- /dev/null
> +++ b/doc/guides/prog_guide/img/rte_flow_q_usage.svg
> @@ -0,0 +1,351 @@
> +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> +<!-- SPDX-License-Identifier: BSD-3-Clause -->
> +
> +<!-- Copyright(c) 2022 NVIDIA Corporation & Affiliates -->
> +
> +<svg
> +   width="880"
> +   height="610"
> +   overflow="hidden"
> +   version="1.1"
> +   id="svg103"
> +   sodipodi:docname="rte_flow_q_usage.svg"
> +   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
> +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
> +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
> +   xmlns="http://www.w3.org/2000/svg"
> +   xmlns:svg="http://www.w3.org/2000/svg">
> +  <sodipodi:namedview
> +     id="namedview105"
> +     pagecolor="#ffffff"
> +     bordercolor="#666666"
> +     borderopacity="1.0"
> +     inkscape:pageshadow="2"
> +     inkscape:pageopacity="0.0"
> +     inkscape:pagecheckerboard="0"
> +     showgrid="false"
> +     inkscape:zoom="1.3311475"
> +     inkscape:cx="439.84606"
> +     inkscape:cy="305.37562"
> +     inkscape:window-width="2400"
> +     inkscape:window-height="1271"
> +     inkscape:window-x="2391"
> +     inkscape:window-y="-9"
> +     inkscape:window-maximized="1"
> +     inkscape:current-layer="g101" />
> +  <defs
> +     id="defs5">
> +    <clipPath
> +       id="clip0">
> +      <rect
> +         x="0"
> +         y="0"
> +         width="880"
> +         height="610"
> +         id="rect2" />
> +    </clipPath>
> +  </defs>
> +  <g
> +     clip-path="url(#clip0)"
> +     id="g101">
> +    <rect
> +       x="0"
> +       y="0"
> +       width="880"
> +       height="610"
> +       fill="#FFFFFF"
> +       id="rect7" />
> +    <rect
> +       x="333.5"
> +       y="0.500053"
> +       width="234"
> +       height="45"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#A6A6A6"
> +       id="rect9" />
> +    <text
> +       font-family="Consolas, Consolas_MSFontService, sans-serif"
> +       font-weight="400"
> +       font-size="19px"
> +       transform="translate(357.196,29)"
> +       id="text11">rte_eth_rx_burst()</text>
> +    <rect
> +       x="333.5"
> +       y="63.5001"
> +       width="234"
> +       height="45"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       id="rect13" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(394.666 91)"
> +       id="text17">analyze <tspan
> +   font-size="19"
> +   x="60.9267"
> +   y="0"
> +   id="tspan15">packet </tspan></text>
> +    <rect
> +       x="572.5"
> +       y="279.5"
> +       width="234"
> +       height="46"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect19" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(591.429 308)"
> +       id="text21">rte_flow_q_flow_create()</text>
> +    <path
> +       d="M333.5 384 450.5 350.5 567.5 384 450.5 417.5Z"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       fill-rule="evenodd"
> +       id="path23" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(430.069 378)"
> +       id="text27">more <tspan
> +   font-size="19"
> +   x="-12.94"
> +   y="23"
> +   id="tspan25">packets?</tspan></text>
> +    <path
> +       d="M689.249 325.5 689.249 338.402 450.5 338.402 450.833 338.069 450.833 343.971 450.167 343.971 450.167 337.735 688.916 337.735 688.582 338.069 688.582 325.5ZM454.5 342.638 450.5 350.638 446.5 342.638Z"
> +       id="path29" />
> +    <path
> +       d="M450.833 45.5 450.833 56.8197 450.167 56.8197 450.167 45.5001ZM454.5 55.4864 450.5 63.4864 446.5 55.4864Z"
> +       id="path31" />
> +    <path
> +       d="M450.833 108.5 450.833 120.375 450.167 120.375 450.167 108.5ZM454.5 119.041 450.5 127.041 446.5 119.041Z"
> +       id="path33" />
> +    <path
> +       d="M451.833 507.5 451.833 533.61 451.167 533.61 451.167 507.5ZM455.5 532.277 451.5 540.277 447.5 532.277Z"
> +       id="path35" />
> +    <path
> +       d="M0 0.333333-23.9993 0.333333-23.666 0-23.666 141.649-23.9993 141.316 562.966 141.316 562.633 141.649 562.633 124.315 563.299 124.315 563.299 141.983-24.3327 141.983-24.3327-0.333333 0-0.333333ZM558.966 125.649 562.966 117.649 566.966 125.649Z"
> +       transform="matrix(-6.12323e-17 -1 -1 6.12323e-17 451.149 585.466)"
> +       id="path37" />
> +    <path
> +       d="M333.5 160.5 450.5 126.5 567.5 160.5 450.5 194.5Z"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       fill-rule="evenodd"
> +       id="path39" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(417.576 155)"
> +       id="text43">add new <tspan
> +   font-size="19"
> +   x="13.2867"
> +   y="23"
> +   id="tspan41">rule?</tspan></text>
> +    <path
> +       d="M567.5 160.167 689.267 160.167 689.267 273.228 688.6 273.228 688.6 160.5 688.933 160.833 567.5 160.833ZM692.933 271.894 688.933 279.894 684.933 271.894Z"
> +       id="path45" />
> +    <rect
> +       x="602.5"
> +       y="127.5"
> +       width="46"
> +       height="30"
> +       stroke="#000000"
> +       stroke-width="0.666667"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       id="rect47" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(611.34 148)"
> +       id="text49">yes</text>
> +    <rect
> +       x="254.5"
> +       y="126.5"
> +       width="46"
> +       height="31"
> +       stroke="#000000"
> +       stroke-width="0.666667"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       id="rect51" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(267.182 147)"
> +       id="text53">no</text>
> +    <path
> +       d="M0-0.333333 251.563-0.333333 251.563 298.328 8.00002 298.328 8.00002 297.662 251.229 297.662 250.896 297.995 250.896 0 251.229 0.333333 0 0.333333ZM9.33333 301.995 1.33333 297.995 9.33333 293.995Z"
> +       transform="matrix(1 0 0 -1 567.5 383.495)"
> +       id="path55" />
> +    <path
> +       d="M86.5001 213.5 203.5 180.5 320.5 213.5 203.5 246.5Z"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       fill-rule="evenodd"
> +       id="path57" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(159.155 208)"
> +       id="text61">destroy the <tspan
> +   font-size="19"
> +   x="24.0333"
> +   y="23"
> +   id="tspan59">rule?</tspan></text>
> +    <path
> +       d="M0-0.333333 131.029-0.333333 131.029 12.9778 130.363 12.9778 130.363 0 130.696 0.333333 0 0.333333ZM134.696 11.6445 130.696 19.6445 126.696 11.6445Z"
> +       transform="matrix(-1 1.22465e-16 1.22465e-16 1 334.196 160.5)"
> +       id="path63" />
> +    <rect
> +       x="81.5001"
> +       y="280.5"
> +       width="234"
> +       height="45"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect65" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(96.2282 308)"
> +       id="text67">rte_flow_q_flow_destroy()</text>
> +    <path
> +       d="M0 0.333333-24.0001 0.333333-23.6667 0-23.6667 49.9498-24.0001 49.6165 121.748 49.6165 121.748 59.958 121.082 59.958 121.082 49.9498 121.415 50.2832-24.3334 50.2832-24.3334-0.333333 0-0.333333ZM125.415 58.6247 121.415 66.6247 117.415 58.6247Z"
> +       transform="matrix(-1 0 0 1 319.915 213.5)"
> +       id="path69" />
> +    <path
> +       d="M86.5001 213.833 62.5002 213.833 62.8335 213.5 62.8335 383.95 62.5002 383.617 327.511 383.617 327.511 384.283 62.1668 384.283 62.1668 213.167 86.5001 213.167ZM326.178 379.95 334.178 383.95 326.178 387.95Z"
> +       id="path71" />
> +    <path
> +       d="M0-0.333333 12.8273-0.333333 12.8273 252.111 12.494 251.778 18.321 251.778 18.321 252.445 12.1607 252.445 12.1607 0 12.494 0.333333 0 0.333333ZM16.9877 248.111 24.9877 252.111 16.9877 256.111Z"
> +       transform="matrix(1.83697e-16 1 1 -1.83697e-16 198.5 325.5)"
> +       id="path73" />
> +    <rect
> +       x="334.5"
> +       y="540.5"
> +       width="234"
> +       height="45"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect75" />
> +    <text
> +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> +       font-weight="400"
> +       font-size="19px"
> +       id="text77"
> +       x="385.08301"
> +       y="569">rte_flow_q_pull()</text>
> +    <rect
> +       x="334.5"
> +       y="462.5"
> +       width="234"
> +       height="45"
> +       stroke="#000000"
> +       stroke-width="1.33333"
> +       stroke-miterlimit="8"
> +       fill="#FFFFFF"
> +       id="rect79" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(379.19 491)"
> +       id="text81">rte_flow_q_push()</text>
> +    <path
> +       d="M450.833 417.495 451.402 455.999 450.735 456.008 450.167 417.505ZM455.048 454.611 451.167 462.669 447.049 454.729Z"
> +       id="path83" />
> +    <rect
> +       x="0.500053"
> +       y="287.5"
> +       width="46"
> +       height="30"
> +       stroke="#000000"
> +       stroke-width="0.666667"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       id="rect85" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(12.8617 308)"
> +       id="text87">no</text>
> +    <rect
> +       x="357.5"
> +       y="223.5"
> +       width="47"
> +       height="31"
> +       stroke="#000000"
> +       stroke-width="0.666667"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       id="rect89" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(367.001 244)"
> +       id="text91">yes</text>
> +    <rect
> +       x="469.5"
> +       y="421.5"
> +       width="46"
> +       height="30"
> +       stroke="#000000"
> +       stroke-width="0.666667"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       id="rect93" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(481.872 442)"
> +       id="text95">no</text>
> +    <rect
> +       x="832.5"
> +       y="223.5"
> +       width="46"
> +       height="31"
> +       stroke="#000000"
> +       stroke-width="0.666667"
> +       stroke-miterlimit="8"
> +       fill="#D9D9D9"
> +       id="rect97" />
> +    <text
> +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> +       font-weight="400"
> +       font-size="19"
> +       transform="translate(841.777 244)"
> +       id="text99">yes</text>
> +  </g>
> +</svg>
> diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
> index 5391648833..5d47f3bd21 100644
> --- a/doc/guides/prog_guide/rte_flow.rst
> +++ b/doc/guides/prog_guide/rte_flow.rst
> @@ -3607,12 +3607,16 @@ Expected number of counters or meters in an application, for example,
>   allow PMD to prepare and optimize NIC memory layout in advance.
>   ``rte_flow_configure()`` must be called before any flow rule is created,
>   but after an Ethernet device is configured.
> +It also creates flow queues for asynchronous flow rules operations via
> +queue-based API, see `Asynchronous operations`_ section.
>   
>   .. code-block:: c
>   
>      int
>      rte_flow_configure(uint16_t port_id,
>                        const struct rte_flow_port_attr *port_attr,
> +                     uint16_t nb_queue,
> +                     const struct rte_flow_queue_attr *queue_attr[],
>                        struct rte_flow_error *error);
>   
>   Information about resources that can benefit from pre-allocation can be
> @@ -3737,7 +3741,7 @@ and pattern and actions templates are created.
>   
>   .. code-block:: c
>   
> -	rte_flow_configure(port, *port_attr, *error);
> +	rte_flow_configure(port, *port_attr, nb_queue, *queue_attr, *error);

* before queue_attr looks strange

>   
>   	struct rte_flow_pattern_template *pattern_templates[0] =
>   		rte_flow_pattern_template_create(port, &itr, &pattern, &error);
> @@ -3750,6 +3754,167 @@ and pattern and actions templates are created.
>   				*actions_templates, nb_actions_templates,
>   				*error);
>   
> +Asynchronous operations
> +-----------------------
> +
> +Flow rules management can be done via special lockless flow management queues.
> +- Queue operations are asynchronous and not thread-safe.
> +
> +- Operations can thus be invoked by the app's datapath,
> +  packet processing can continue while queue operations are processed by NIC.
> +
> +- The queue number is configured at initialization stage.

I read "the queue number" as some number for a specific queue.
May be "Number of queues is configured..."

> +
> +- Available operation types: rule creation, rule destruction,
> +  indirect rule creation, indirect rule destruction, indirect rule update.
> +
> +- Operations may be reordered within a queue.

Do we want to have barriers?
E.g. create rule, destroy the same rule -> reoder -> destroy fails, rule 
lives forever.

> +
> +- Operations can be postponed and pushed to NIC in batches.
> +
> +- Results pulling must be done on time to avoid queue overflows.

polling? (as libc poll() which checks status of file descriptors)
it is not pulling the door to open it :)

> +
> +- User data is returned as part of the result to identify an operation.
> +
> +- Flow handle is valid once the creation operation is enqueued and must be
> +  destroyed even if the operation is not successful and the rule is not inserted.
> +
> +The asynchronous flow rule insertion logic can be broken into two phases.
> +
> +1. Initialization stage as shown here:
> +
> +.. _figure_rte_flow_q_init:
> +
> +.. figure:: img/rte_flow_q_init.*
> +
> +2. Main loop as presented on a datapath application example:
> +
> +.. _figure_rte_flow_q_usage:
> +
> +.. figure:: img/rte_flow_q_usage.*
> +
> +Enqueue creation operation
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Enqueueing a flow rule creation operation is similar to simple creation.
> +
> +.. code-block:: c
> +
> +	struct rte_flow *
> +	rte_flow_q_flow_create(uint16_t port_id,
> +				uint32_t queue_id,
> +				const struct rte_flow_q_ops_attr *q_ops_attr,
> +				struct rte_flow_template_table *template_table,
> +				const struct rte_flow_item pattern[],
> +				uint8_t pattern_template_index,
> +				const struct rte_flow_action actions[],
> +				uint8_t actions_template_index,
> +				struct rte_flow_error *error);
> +
> +A valid handle in case of success is returned. It must be destroyed later
> +by calling ``rte_flow_q_flow_destroy()`` even if the rule is rejected by HW.
> +
> +Enqueue destruction operation
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Enqueueing a flow rule destruction operation is similar to simple destruction.
> +
> +.. code-block:: c
> +
> +	int
> +	rte_flow_q_flow_destroy(uint16_t port_id,
> +				uint32_t queue_id,
> +				const struct rte_flow_q_ops_attr *q_ops_attr,
> +				struct rte_flow *flow,
> +				struct rte_flow_error *error);
> +
> +Push enqueued operations
> +~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Pushing all internally stored rules from a queue to the NIC.
> +
> +.. code-block:: c
> +
> +	int
> +	rte_flow_q_push(uint16_t port_id,
> +			uint32_t queue_id,
> +			struct rte_flow_error *error);
> +
> +There is the postpone attribute in the queue operation attributes.
> +When it is set, multiple operations can be bulked together and not sent to HW
> +right away to save SW/HW interactions and prioritize throughput over latency.
> +The application must invoke this function to actually push all outstanding
> +operations to HW in this case.
> +
> +Pull enqueued operations
> +~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Pulling asynchronous operations results.
> +
> +The application must invoke this function in order to complete asynchronous
> +flow rule operations and to receive flow rule operations statuses.
> +
> +.. code-block:: c
> +
> +	int
> +	rte_flow_q_pull(uint16_t port_id,
> +			uint32_t queue_id,
> +			struct rte_flow_q_op_res res[],
> +			uint16_t n_res,
> +			struct rte_flow_error *error);
> +
> +Multiple outstanding operation results can be pulled simultaneously.
> +User data may be provided during a flow creation/destruction in order
> +to distinguish between multiple operations. User data is returned as part
> +of the result to provide a method to detect which operation is completed.
> +
> +Enqueue indirect action creation operation
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Asynchronous version of indirect action creation API.
> +
> +.. code-block:: c
> +
> +	struct rte_flow_action_handle *
> +	rte_flow_q_action_handle_create(uint16_t port_id,
> +			uint32_t queue_id,
> +			const struct rte_flow_q_ops_attr *q_ops_attr,
> +			const struct rte_flow_indir_action_conf *indir_action_conf,
> +			const struct rte_flow_action *action,
> +			struct rte_flow_error *error);
> +
> +A valid handle in case of success is returned. It must be destroyed later by
> +calling ``rte_flow_q_action_handle_destroy()`` even if the rule is rejected.
> +
> +Enqueue indirect action destruction operation
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Asynchronous version of indirect action destruction API.
> +
> +.. code-block:: c
> +
> +	int
> +	rte_flow_q_action_handle_destroy(uint16_t port_id,
> +			uint32_t queue_id,
> +			const struct rte_flow_q_ops_attr *q_ops_attr,
> +			struct rte_flow_action_handle *action_handle,
> +			struct rte_flow_error *error);
> +
> +Enqueue indirect action update operation
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Asynchronous version of indirect action update API.
> +
> +.. code-block:: c
> +
> +	int
> +	rte_flow_q_action_handle_update(uint16_t port_id,
> +			uint32_t queue_id,
> +			const struct rte_flow_q_ops_attr *q_ops_attr,
> +			struct rte_flow_action_handle *action_handle,
> +			const void *update,
> +			struct rte_flow_error *error);
> +
>   .. _flow_isolated_mode:
>   
>   Flow isolated mode
> diff --git a/doc/guides/rel_notes/release_22_03.rst b/doc/guides/rel_notes/release_22_03.rst
> index 6656b35295..87cea8a966 100644
> --- a/doc/guides/rel_notes/release_22_03.rst
> +++ b/doc/guides/rel_notes/release_22_03.rst
> @@ -83,6 +83,14 @@ New Features
>       ``rte_flow_template_table_destroy``, ``rte_flow_pattern_template_destroy``
>       and ``rte_flow_actions_template_destroy``.
>   
> +  * ethdev: Added ``rte_flow_q_flow_create`` and ``rte_flow_q_flow_destroy``
> +    API to enqueue flow creaion/destruction operations asynchronously as well
> +    as ``rte_flow_q_pull`` to poll and retrieve results of these operations
> +    and ``rte_flow_q_push`` to push all the in-flight operations to the NIC.
> +    Introduced asynchronous API for indirect actions management as well:
> +    ``rte_flow_q_action_handle_create``, ``rte_flow_q_action_handle_destroy``
> +    and ``rte_flow_q_action_handle_update``.
> +
>   * **Updated AF_XDP PMD**
>   
>     * Added support for libxdp >=v1.2.2.
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> index b53f8c9b89..aca5bac2da 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -1415,6 +1415,8 @@ rte_flow_info_get(uint16_t port_id,
>   int
>   rte_flow_configure(uint16_t port_id,
>   		   const struct rte_flow_port_attr *port_attr,
> +		   uint16_t nb_queue,
> +		   const struct rte_flow_queue_attr *queue_attr[],
>   		   struct rte_flow_error *error)
>   {
>   	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> @@ -1424,7 +1426,8 @@ rte_flow_configure(uint16_t port_id,
>   		return -rte_errno;
>   	if (likely(!!ops->configure)) {
>   		return flow_err(port_id,
> -				ops->configure(dev, port_attr, error),
> +				ops->configure(dev, port_attr,
> +					       nb_queue, queue_attr, error),
>   				error);
>   	}
>   	return rte_flow_error_set(error, ENOTSUP,
> @@ -1578,3 +1581,173 @@ rte_flow_template_table_destroy(uint16_t port_id,
>   				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
>   				  NULL, rte_strerror(ENOTSUP));
>   }
> +
> +struct rte_flow *
> +rte_flow_q_flow_create(uint16_t port_id,
> +		       uint32_t queue_id,
> +		       const struct rte_flow_q_ops_attr *q_ops_attr,
> +		       struct rte_flow_template_table *template_table,
> +		       const struct rte_flow_item pattern[],
> +		       uint8_t pattern_template_index,
> +		       const struct rte_flow_action actions[],
> +		       uint8_t actions_template_index,
> +		       struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +	struct rte_flow *flow;
> +
> +	if (unlikely(!ops))
> +		return NULL;
> +	if (likely(!!ops->q_flow_create)) {
> +		flow = ops->q_flow_create(dev, queue_id,
> +					  q_ops_attr, template_table,
> +					  pattern, pattern_template_index,
> +					  actions, actions_template_index,
> +					  error);
> +		if (flow == NULL)
> +			flow_err(port_id, -rte_errno, error);
> +		return flow;
> +	}
> +	rte_flow_error_set(error, ENOTSUP,
> +			   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +			   NULL, rte_strerror(ENOTSUP));
> +	return NULL;
> +}
> +
> +int
> +rte_flow_q_flow_destroy(uint16_t port_id,
> +			uint32_t queue_id,
> +			const struct rte_flow_q_ops_attr *q_ops_attr,
> +			struct rte_flow *flow,
> +			struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +
> +	if (unlikely(!ops))
> +		return -rte_errno;
> +	if (likely(!!ops->q_flow_destroy)) {
> +		return flow_err(port_id,
> +				ops->q_flow_destroy(dev, queue_id,
> +						    q_ops_attr, flow, error),
> +				error);
> +	}
> +	return rte_flow_error_set(error, ENOTSUP,
> +				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				  NULL, rte_strerror(ENOTSUP));
> +}
> +
> +struct rte_flow_action_handle *
> +rte_flow_q_action_handle_create(uint16_t port_id,
> +		uint32_t queue_id,
> +		const struct rte_flow_q_ops_attr *q_ops_attr,
> +		const struct rte_flow_indir_action_conf *indir_action_conf,
> +		const struct rte_flow_action *action,
> +		struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +	struct rte_flow_action_handle *handle;
> +
> +	if (unlikely(!ops))
> +		return NULL;
> +	if (unlikely(!ops->q_action_handle_create)) {
> +		rte_flow_error_set(error, ENOSYS,
> +				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
> +				   rte_strerror(ENOSYS));
> +		return NULL;
> +	}
> +	handle = ops->q_action_handle_create(dev, queue_id, q_ops_attr,
> +					     indir_action_conf, action, error);
> +	if (handle == NULL)
> +		flow_err(port_id, -rte_errno, error);
> +	return handle;
> +}
> +
> +int
> +rte_flow_q_action_handle_destroy(uint16_t port_id,
> +		uint32_t queue_id,
> +		const struct rte_flow_q_ops_attr *q_ops_attr,
> +		struct rte_flow_action_handle *action_handle,
> +		struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +	int ret;
> +
> +	if (unlikely(!ops))
> +		return -rte_errno;
> +	if (unlikely(!ops->q_action_handle_destroy))
> +		return rte_flow_error_set(error, ENOSYS,
> +					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +					  NULL, rte_strerror(ENOSYS));
> +	ret = ops->q_action_handle_destroy(dev, queue_id, q_ops_attr,
> +					   action_handle, error);
> +	return flow_err(port_id, ret, error);
> +}
> +
> +int
> +rte_flow_q_action_handle_update(uint16_t port_id,
> +		uint32_t queue_id,
> +		const struct rte_flow_q_ops_attr *q_ops_attr,
> +		struct rte_flow_action_handle *action_handle,
> +		const void *update,
> +		struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +	int ret;
> +
> +	if (unlikely(!ops))
> +		return -rte_errno;
> +	if (unlikely(!ops->q_action_handle_update))
> +		return rte_flow_error_set(error, ENOSYS,
> +					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +					  NULL, rte_strerror(ENOSYS));
> +	ret = ops->q_action_handle_update(dev, queue_id, q_ops_attr,
> +					  action_handle, update, error);
> +	return flow_err(port_id, ret, error);
> +}
> +
> +int
> +rte_flow_q_push(uint16_t port_id,
> +		uint32_t queue_id,
> +		struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +
> +	if (unlikely(!ops))
> +		return -rte_errno;
> +	if (likely(!!ops->q_push)) {
> +		return flow_err(port_id,
> +				ops->q_push(dev, queue_id, error),
> +				error);
> +	}
> +	return rte_flow_error_set(error, ENOTSUP,
> +				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				  NULL, rte_strerror(ENOTSUP));
> +}
> +
> +int
> +rte_flow_q_pull(uint16_t port_id,
> +		uint32_t queue_id,
> +		struct rte_flow_q_op_res res[],
> +		uint16_t n_res,
> +		struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +	int ret;
> +
> +	if (unlikely(!ops))
> +		return -rte_errno;
> +	if (likely(!!ops->q_pull)) {
> +		ret = ops->q_pull(dev, queue_id, res, n_res, error);
> +		return ret ? ret : flow_err(port_id, ret, error);
> +	}
> +	return rte_flow_error_set(error, ENOTSUP,
> +				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> +				  NULL, rte_strerror(ENOTSUP));
> +}
> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index e87db5a540..b0d4f33bfd 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -4862,6 +4862,10 @@ rte_flow_flex_item_release(uint16_t port_id,
>    *
>    */
>   struct rte_flow_port_info {
> +	/**
> +	 * Number of queues for asynchronous operations.

Is it a maximum number of queues?

> +	 */
> +	uint32_t nb_queues;
>   	/**
>   	 * Number of pre-configurable counter actions.
>   	 * @see RTE_FLOW_ACTION_TYPE_COUNT
> @@ -4879,6 +4883,17 @@ struct rte_flow_port_info {
>   	uint32_t nb_meters;
>   };
>   
> +/**
> + * Flow engine queue configuration.
> + */
> +__extension__
> +struct rte_flow_queue_attr {
> +	/**
> +	 * Number of flow rule operations a queue can hold.
> +	 */
> +	uint32_t size;

Whar are the min/max sizes? 0 as the default size, if yes, do we need
an API to find actual size?

> +};
> +
>   /**
>    * @warning
>    * @b EXPERIMENTAL: this API may change without prior notice.
> @@ -4948,6 +4963,11 @@ struct rte_flow_port_attr {
>    *   Port identifier of Ethernet device.
>    * @param[in] port_attr
>    *   Port configuration attributes.
> + * @param[in] nb_queue
> + *   Number of flow queues to be configured.
> + * @param[in] queue_attr
> + *   Array that holds attributes for each flow queue.
> + *   Number of elements is set in @p port_attr.nb_queues.
>    * @param[out] error
>    *   Perform verbose error reporting if not NULL.
>    *   PMDs initialize this structure in case of error only.
> @@ -4959,6 +4979,8 @@ __rte_experimental
>   int
>   rte_flow_configure(uint16_t port_id,
>   		   const struct rte_flow_port_attr *port_attr,
> +		   uint16_t nb_queue,
> +		   const struct rte_flow_queue_attr *queue_attr[],
>   		   struct rte_flow_error *error);
>   
>   /**
> @@ -5221,6 +5243,318 @@ rte_flow_template_table_destroy(uint16_t port_id,
>   		struct rte_flow_template_table *template_table,
>   		struct rte_flow_error *error);
>   
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Queue operation attributes.
> + */
> +struct rte_flow_q_ops_attr {
> +	/**
> +	 * The user data that will be returned on the completion events.
> +	 */
> +	void *user_data;

IMHO it must not be hiddne in attrs. It is a key information
which is used to understand the opration result. It should
be passed separately.

> +	 /**
> +	  * When set, the requested action will not be sent to the HW immediately.
> +	  * The application must call the rte_flow_queue_push to actually send it.

Will the next operation without the attribute set implicitly push it?
Is it mandatory for the driver to respect it? Or is it just a possible
optimization hint?

> +	  */
> +	uint32_t postpone:1;
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue rule creation operation.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue used to insert the rule.
> + * @param[in] q_ops_attr
> + *   Rule creation operation attributes.
> + * @param[in] template_table
> + *   Template table to select templates from.

IMHO it should be done optional. I.e. NULL allows.
If NULL, indecies are ignored and pattern+actions are full
specificiation as in rte_flow_create(). The only missing bit
is attributes.

Basically I'm sure that hardwiring queue-based flow rule control
to template is the right solution. It should be possible without
templates. May be it should be a separate API to be added later
if/when required.

> + * @param[in] pattern
> + *   List of pattern items to be used.
> + *   The list order should match the order in the pattern template.
> + *   The spec is the only relevant member of the item that is being used.
> + * @param[in] pattern_template_index
> + *   Pattern template index in the table.
> + * @param[in] actions
> + *   List of actions to be used.
> + *   The list order should match the order in the actions template.
> + * @param[in] actions_template_index
> + *   Actions template index in the table.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   Handle on success, NULL otherwise and rte_errno is set.
> + *   The rule handle doesn't mean that the rule was offloaded.

"was offloaded" sounds ambiguous. API says nothing about any kind
of offloading before. "has been populated" or "has been
created" (since API says "create").

> + *   Only completion result indicates that the rule was offloaded.
> + */
> +__rte_experimental
> +struct rte_flow *
> +rte_flow_q_flow_create(uint16_t port_id,

flow_q_flow does not sound like a good nameing, consider:
rte_flow_q_rule_create() is <subsystem>_<subtype>_<object>_<action>

> +		       uint32_t queue_id,
> +		       const struct rte_flow_q_ops_attr *q_ops_attr,
> +		       struct rte_flow_template_table *template_table,
> +		       const struct rte_flow_item pattern[],
> +		       uint8_t pattern_template_index,
> +		       const struct rte_flow_action actions[],
> +		       uint8_t actions_template_index,
> +		       struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue rule destruction operation.
> + *
> + * This function enqueues a destruction operation on the queue.
> + * Application should assume that after calling this function
> + * the rule handle is not valid anymore.
> + * Completion indicates the full removal of the rule from the HW.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue which is used to destroy the rule.
> + *   This must match the queue on which the rule was created.
> + * @param[in] q_ops_attr
> + *   Rule destroy operation attributes.
> + * @param[in] flow
> + *   Flow handle to be destroyed.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   0 on success, a negative errno value otherwise and rte_errno is set.
> + */
> +__rte_experimental
> +int
> +rte_flow_q_flow_destroy(uint16_t port_id,
> +			uint32_t queue_id,
> +			const struct rte_flow_q_ops_attr *q_ops_attr,
> +			struct rte_flow *flow,
> +			struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue indirect action creation operation.
> + * @see rte_flow_action_handle_create
> + *
> + * @param[in] port_id
> + *   Port identifier of Ethernet device.
> + * @param[in] queue_id
> + *   Flow queue which is used to create the rule.
> + * @param[in] q_ops_attr
> + *   Queue operation attributes.
> + * @param[in] indir_action_conf
> + *   Action configuration for the indirect action object creation.
> + * @param[in] action
> + *   Specific configuration of the indirect action object.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   - (0) if success.

Hold on. Pointer is returned by the function.

> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-ENOSYS) if underlying device does not support this functionality.
> + *   - (-EIO) if underlying device is removed.
> + *   - (-ENOENT) if action pointed by *action* handle was not found.
> + *   - (-EBUSY) if action pointed by *action* handle still used by some rules
> + *   rte_errno is also set.

Which error code should be used if too many ops are enqueued (overflow)?

> + */
> +__rte_experimental
> +struct rte_flow_action_handle *
> +rte_flow_q_action_handle_create(uint16_t port_id,
> +		uint32_t queue_id,
> +		const struct rte_flow_q_ops_attr *q_ops_attr,
> +		const struct rte_flow_indir_action_conf *indir_action_conf,
> +		const struct rte_flow_action *action,

I don't understand why it differs so much from rule creation.
Why is action template not used?
IMHO indirect actions should be dropped from the patch
and added separately since it is a separate feature.

> +		struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue indirect action destruction operation.
> + * The destroy queue must be the same
> + * as the queue on which the action was created.
> + *
> + * @param[in] port_id
> + *   Port identifier of Ethernet device.
> + * @param[in] queue_id
> + *   Flow queue which is used to destroy the rule.
> + * @param[in] q_ops_attr
> + *   Queue operation attributes.
> + * @param[in] action_handle
> + *   Handle for the indirect action object to be destroyed.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   - (0) if success.
> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-ENOSYS) if underlying device does not support this functionality.
> + *   - (-EIO) if underlying device is removed.
> + *   - (-ENOENT) if action pointed by *action* handle was not found.
> + *   - (-EBUSY) if action pointed by *action* handle still used by some rules
> + *   rte_errno is also set.
> + */
> +__rte_experimental
> +int
> +rte_flow_q_action_handle_destroy(uint16_t port_id,
> +		uint32_t queue_id,
> +		const struct rte_flow_q_ops_attr *q_ops_attr,
> +		struct rte_flow_action_handle *action_handle,
> +		struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue indirect action update operation.
> + * @see rte_flow_action_handle_create
> + *
> + * @param[in] port_id
> + *   Port identifier of Ethernet device.
> + * @param[in] queue_id
> + *   Flow queue which is used to update the rule.
> + * @param[in] q_ops_attr
> + *   Queue operation attributes.
> + * @param[in] action_handle
> + *   Handle for the indirect action object to be updated.
> + * @param[in] update
> + *   Update profile specification used to modify the action pointed by handle.
> + *   *update* could be with the same type of the immediate action corresponding
> + *   to the *handle* argument when creating, or a wrapper structure includes
> + *   action configuration to be updated and bit fields to indicate the member
> + *   of fields inside the action to update.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   - (0) if success.
> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-ENOSYS) if underlying device does not support this functionality.
> + *   - (-EIO) if underlying device is removed.
> + *   - (-ENOENT) if action pointed by *action* handle was not found.
> + *   - (-EBUSY) if action pointed by *action* handle still used by some rules
> + *   rte_errno is also set.
> + */
> +__rte_experimental
> +int
> +rte_flow_q_action_handle_update(uint16_t port_id,
> +		uint32_t queue_id,
> +		const struct rte_flow_q_ops_attr *q_ops_attr,
> +		struct rte_flow_action_handle *action_handle,
> +		const void *update,
> +		struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Push all internally stored rules to the HW.
> + * Postponed rules are rules that were inserted with the postpone flag set.
> + * Can be used to notify the HW about batch of rules prepared by the SW to
> + * reduce the number of communications between the HW and SW.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue to be pushed.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *    0 on success, a negative errno value otherwise and rte_errno is set.
> + */
> +__rte_experimental
> +int
> +rte_flow_q_push(uint16_t port_id,
> +		uint32_t queue_id,
> +		struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Queue operation status.
> + */
> +enum rte_flow_q_op_status {
> +	/**
> +	 * The operation was completed successfully.
> +	 */
> +	RTE_FLOW_Q_OP_SUCCESS,
> +	/**
> +	 * The operation was not completed successfully.
> +	 */
> +	RTE_FLOW_Q_OP_ERROR,
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Queue operation results.
> + */
> +__extension__
> +struct rte_flow_q_op_res {
> +	/**
> +	 * Returns the status of the operation that this completion signals.
> +	 */
> +	enum rte_flow_q_op_status status;
> +	/**
> +	 * The user data that will be returned on the completion events.
> +	 */
> +	void *user_data;
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Pull a rte flow operation.
> + * The application must invoke this function in order to complete
> + * the flow rule offloading and to retrieve the flow rule operation status.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue which is used to pull the operation.
> + * @param[out] res
> + *   Array of results that will be set.
> + * @param[in] n_res
> + *   Maximum number of results that can be returned.
> + *   This value is equal to the size of the res array.
> + * @param[out] error
> + *   Perform verbose error reporting if not NULL.
> + *   PMDs initialize this structure in case of error only.
> + *
> + * @return
> + *   Number of results that were pulled,
> + *   a negative errno value otherwise and rte_errno is set.

Don't we want to define negative error code meaning?

> + */
> +__rte_experimental
> +int
> +rte_flow_q_pull(uint16_t port_id,
> +		uint32_t queue_id,
> +		struct rte_flow_q_op_res res[],
> +		uint16_t n_res,
> +		struct rte_flow_error *error);
> +
>   #ifdef __cplusplus
>   }
>   #endif
  
Alexander Kozyrev Feb. 12, 2022, 2:19 a.m. UTC | #2
On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> On 2/11/22 05:26, Alexander Kozyrev wrote:
> > A new, faster, queue-based flow rules management mechanism is needed
> for
> > applications offloading rules inside the datapath. This asynchronous
> > and lockless mechanism frees the CPU for further packet processing and
> > reduces the performance impact of the flow rules creation/destruction
> > on the datapath. Note that queues are not thread-safe and the queue
> > should be accessed from the same thread for all queue operations.
> > It is the responsibility of the app to sync the queue functions in case
> > of multi-threaded access to the same queue.
> >
> > The rte_flow_q_flow_create() function enqueues a flow creation to the
> > requested queue. It benefits from already configured resources and sets
> > unique values on top of item and action templates. A flow rule is enqueued
> > on the specified flow queue and offloaded asynchronously to the
> hardware.
> > The function returns immediately to spare CPU for further packet
> > processing. The application must invoke the rte_flow_q_pull() function
> > to complete the flow rule operation offloading, to clear the queue, and to
> > receive the operation status. The rte_flow_q_flow_destroy() function
> > enqueues a flow destruction to the requested queue.
> >
> > Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
> > Acked-by: Ori Kam <orika@nvidia.com>
> > ---
> >   doc/guides/prog_guide/img/rte_flow_q_init.svg | 205 ++++++++++
> >   .../prog_guide/img/rte_flow_q_usage.svg       | 351
> ++++++++++++++++++
> >   doc/guides/prog_guide/rte_flow.rst            | 167 ++++++++-
> >   doc/guides/rel_notes/release_22_03.rst        |   8 +
> >   lib/ethdev/rte_flow.c                         | 175 ++++++++-
> >   lib/ethdev/rte_flow.h                         | 334 +++++++++++++++++
> >   lib/ethdev/rte_flow_driver.h                  |  55 +++
> >   lib/ethdev/version.map                        |   7 +
> >   8 files changed, 1300 insertions(+), 2 deletions(-)
> >   create mode 100644 doc/guides/prog_guide/img/rte_flow_q_init.svg
> >   create mode 100644 doc/guides/prog_guide/img/rte_flow_q_usage.svg
> >
> > diff --git a/doc/guides/prog_guide/img/rte_flow_q_init.svg
> b/doc/guides/prog_guide/img/rte_flow_q_init.svg
> > new file mode 100644
> > index 0000000000..96160bde42
> > --- /dev/null
> > +++ b/doc/guides/prog_guide/img/rte_flow_q_init.svg
> > @@ -0,0 +1,205 @@
> > +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> > +<!-- SPDX-License-Identifier: BSD-3-Clause -->
> > +
> > +<!-- Copyright(c) 2022 NVIDIA Corporation & Affiliates -->
> > +
> > +<svg
> > +   width="485"
> > +   height="535"
> > +   overflow="hidden"
> > +   version="1.1"
> > +   id="svg61"
> > +   sodipodi:docname="rte_flow_q_init.svg"
> > +   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
> > +
> xmlns:inkscape="https://nam11.safelinks.protection.outlook.com/?url=http
> %3A%2F%2Fwww.inkscape.org%2Fnamespaces%2Finkscape&amp;data=04
> %7C01%7Cakozyrev%40nvidia.com%7C17305c0b25ed4450b31008d9ed5bf333
> %7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C637801801473234111
> %7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiL
> CJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&amp;sdata=xpBAPvwrE5t1eHUc
> YHt%2FzLJ5seo%2F%2FomPwGvplYtAgv0%3D&amp;reserved=0"
> > +
> xmlns:sodipodi="https://nam11.safelinks.protection.outlook.com/?url=http
> %3A%2F%2Fsodipodi.sourceforge.net%2FDTD%2Fsodipodi-
> 0.dtd&amp;data=04%7C01%7Cakozyrev%40nvidia.com%7C17305c0b25ed445
> 0b31008d9ed5bf333%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C
> 637801801473234111%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwM
> DAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&amp;sdata
> =yFZ8tT7Zge2JgcuCKPGPe8MhGNEfsD2fdWpHdkO8qoc%3D&amp;reserved=
> 0"
> > +
> xmlns="https://nam11.safelinks.protection.outlook.com/?url=http%3A%2F
> %2Fwww.w3.org%2F2000%2Fsvg&amp;data=04%7C01%7Cakozyrev%40nvidi
> a.com%7C17305c0b25ed4450b31008d9ed5bf333%7C43083d15727340c1b7db3
> 9efd9ccc17a%7C0%7C0%7C637801801473234111%7CUnknown%7CTWFpbGZs
> b3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn
> 0%3D%7C3000&amp;sdata=e2WMYQltdZPW%2FaZEm0RgJXXyaRFxJH%2B279
> J1xTp9eg4%3D&amp;reserved=0"
> > +
> xmlns:svg="https://nam11.safelinks.protection.outlook.com/?url=http%3A%
> 2F%2Fwww.w3.org%2F2000%2Fsvg&amp;data=04%7C01%7Cakozyrev%40nvi
> dia.com%7C17305c0b25ed4450b31008d9ed5bf333%7C43083d15727340c1b7d
> b39efd9ccc17a%7C0%7C0%7C637801801473234111%7CUnknown%7CTWFpb
> GZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI
> 6Mn0%3D%7C3000&amp;sdata=e2WMYQltdZPW%2FaZEm0RgJXXyaRFxJH%2
> B279J1xTp9eg4%3D&amp;reserved=0">
> > +  <sodipodi:namedview
> > +     id="namedview63"
> > +     pagecolor="#ffffff"
> > +     bordercolor="#666666"
> > +     borderopacity="1.0"
> > +     inkscape:pageshadow="2"
> > +     inkscape:pageopacity="0.0"
> > +     inkscape:pagecheckerboard="0"
> > +     showgrid="false"
> > +     inkscape:zoom="1.517757"
> > +     inkscape:cx="242.79249"
> > +     inkscape:cy="267.17057"
> > +     inkscape:window-width="2400"
> > +     inkscape:window-height="1271"
> > +     inkscape:window-x="2391"
> > +     inkscape:window-y="-9"
> > +     inkscape:window-maximized="1"
> > +     inkscape:current-layer="g59" />
> > +  <defs
> > +     id="defs5">
> > +    <clipPath
> > +       id="clip0">
> > +      <rect
> > +         x="0"
> > +         y="0"
> > +         width="485"
> > +         height="535"
> > +         id="rect2" />
> > +    </clipPath>
> > +  </defs>
> > +  <g
> > +     clip-path="url(#clip0)"
> > +     id="g59">
> > +    <rect
> > +       x="0"
> > +       y="0"
> > +       width="485"
> > +       height="535"
> > +       fill="#FFFFFF"
> > +       id="rect7" />
> > +    <rect
> > +       x="0.500053"
> > +       y="79.5001"
> > +       width="482"
> > +       height="59"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#A6A6A6"
> > +       id="rect9" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="24"
> > +       transform="translate(121.6 116)"
> > +       id="text13">
> > +         rte_eth_dev_configure
> > +         <tspan
> > +   font-size="24"
> > +   x="224.007"
> > +   y="0"
> > +   id="tspan11">()</tspan></text>
> > +    <rect
> > +       x="0.500053"
> > +       y="158.5"
> > +       width="482"
> > +       height="59"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect15" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="24"
> > +       transform="translate(140.273 195)"
> > +       id="text17">
> > +         rte_flow_configure()
> > +      </text>
> > +    <rect
> > +       x="0.500053"
> > +       y="236.5"
> > +       width="482"
> > +       height="60"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect19" />
> > +    <text
> > +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> > +       font-weight="400"
> > +       font-size="24px"
> > +       id="text21"
> > +       x="63.425903"
> > +       y="274">rte_flow_pattern_template_create()</text>
> > +    <rect
> > +       x="0.500053"
> > +       y="316.5"
> > +       width="482"
> > +       height="59"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect23" />
> > +    <text
> > +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> > +       font-weight="400"
> > +       font-size="24px"
> > +       id="text27"
> > +       x="69.379204"
> > +       y="353">rte_flow_actions_template_create()</text>
> > +    <rect
> > +       x="0.500053"
> > +       y="0.500053"
> > +       width="482"
> > +       height="60"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#A6A6A6"
> > +       id="rect29" />
> > +    <text
> > +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> > +       font-weight="400"
> > +       font-size="24px"
> > +       transform="translate(177.233,37)"
> > +       id="text33">rte_eal_init()</text>
> > +    <path
> > +       d="M2-1.09108e-05 2.00005 9.2445-1.99995 9.24452-2 1.09108e-
> 05ZM6.00004 7.24448 0.000104987 19.2445-5.99996 7.24455Z"
> > +       transform="matrix(-1 0 0 1 241 60)"
> > +       id="path35" />
> > +    <path
> > +       d="M2-1.08133e-05 2.00005 9.41805-1.99995 9.41807-2 1.08133e-
> 05ZM6.00004 7.41802 0.000104987 19.4181-5.99996 7.41809Z"
> > +       transform="matrix(-1 0 0 1 241 138)"
> > +       id="path37" />
> > +    <path
> > +       d="M2-1.09108e-05 2.00005 9.2445-1.99995 9.24452-2 1.09108e-
> 05ZM6.00004 7.24448 0.000104987 19.2445-5.99996 7.24455Z"
> > +       transform="matrix(-1 0 0 1 241 217)"
> > +       id="path39" />
> > +    <rect
> > +       x="0.500053"
> > +       y="395.5"
> > +       width="482"
> > +       height="59"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect41" />
> > +    <text
> > +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> > +       font-weight="400"
> > +       font-size="24px"
> > +       id="text47"
> > +       x="76.988998"
> > +       y="432">rte_flow_template_table_create()</text>
> > +    <path
> > +       d="M2-1.05859e-05 2.00005 9.83526-1.99995 9.83529-2 1.05859e-
> 05ZM6.00004 7.83524 0.000104987 19.8353-5.99996 7.83531Z"
> > +       transform="matrix(-1 0 0 1 241 296)"
> > +       id="path49" />
> > +    <path
> > +       d="M243 375 243 384.191 239 384.191 239 375ZM247 382.191 241
> 394.191 235 382.191Z"
> > +       id="path51" />
> > +    <rect
> > +       x="0.500053"
> > +       y="473.5"
> > +       width="482"
> > +       height="60"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#A6A6A6"
> > +       id="rect53" />
> > +    <text
> > +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> > +       font-weight="400"
> > +       font-size="24px"
> > +       id="text55"
> > +       x="149.30299"
> > +       y="511">rte_eth_dev_start()</text>
> > +    <path
> > +       d="M245 454 245 463.191 241 463.191 241 454ZM249 461.191 243
> 473.191 237 461.191Z"
> > +       id="path57" />
> > +  </g>
> > +</svg>
> > diff --git a/doc/guides/prog_guide/img/rte_flow_q_usage.svg
> b/doc/guides/prog_guide/img/rte_flow_q_usage.svg
> > new file mode 100644
> > index 0000000000..a1f6c0a0a8
> > --- /dev/null
> > +++ b/doc/guides/prog_guide/img/rte_flow_q_usage.svg
> > @@ -0,0 +1,351 @@
> > +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> > +<!-- SPDX-License-Identifier: BSD-3-Clause -->
> > +
> > +<!-- Copyright(c) 2022 NVIDIA Corporation & Affiliates -->
> > +
> > +<svg
> > +   width="880"
> > +   height="610"
> > +   overflow="hidden"
> > +   version="1.1"
> > +   id="svg103"
> > +   sodipodi:docname="rte_flow_q_usage.svg"
> > +   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
> > +
> xmlns:inkscape="https://nam11.safelinks.protection.outlook.com/?url=http
> %3A%2F%2Fwww.inkscape.org%2Fnamespaces%2Finkscape&amp;data=04
> %7C01%7Cakozyrev%40nvidia.com%7C17305c0b25ed4450b31008d9ed5bf333
> %7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C637801801473234111
> %7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiL
> CJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&amp;sdata=xpBAPvwrE5t1eHUc
> YHt%2FzLJ5seo%2F%2FomPwGvplYtAgv0%3D&amp;reserved=0"
> > +
> xmlns:sodipodi="https://nam11.safelinks.protection.outlook.com/?url=http
> %3A%2F%2Fsodipodi.sourceforge.net%2FDTD%2Fsodipodi-
> 0.dtd&amp;data=04%7C01%7Cakozyrev%40nvidia.com%7C17305c0b25ed445
> 0b31008d9ed5bf333%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C
> 637801801473234111%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwM
> DAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&amp;sdata
> =yFZ8tT7Zge2JgcuCKPGPe8MhGNEfsD2fdWpHdkO8qoc%3D&amp;reserved=
> 0"
> > +
> xmlns="https://nam11.safelinks.protection.outlook.com/?url=http%3A%2F
> %2Fwww.w3.org%2F2000%2Fsvg&amp;data=04%7C01%7Cakozyrev%40nvidi
> a.com%7C17305c0b25ed4450b31008d9ed5bf333%7C43083d15727340c1b7db3
> 9efd9ccc17a%7C0%7C0%7C637801801473234111%7CUnknown%7CTWFpbGZs
> b3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn
> 0%3D%7C3000&amp;sdata=e2WMYQltdZPW%2FaZEm0RgJXXyaRFxJH%2B279
> J1xTp9eg4%3D&amp;reserved=0"
> > +
> xmlns:svg="https://nam11.safelinks.protection.outlook.com/?url=http%3A%
> 2F%2Fwww.w3.org%2F2000%2Fsvg&amp;data=04%7C01%7Cakozyrev%40nvi
> dia.com%7C17305c0b25ed4450b31008d9ed5bf333%7C43083d15727340c1b7d
> b39efd9ccc17a%7C0%7C0%7C637801801473234111%7CUnknown%7CTWFpb
> GZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI
> 6Mn0%3D%7C3000&amp;sdata=e2WMYQltdZPW%2FaZEm0RgJXXyaRFxJH%2
> B279J1xTp9eg4%3D&amp;reserved=0">
> > +  <sodipodi:namedview
> > +     id="namedview105"
> > +     pagecolor="#ffffff"
> > +     bordercolor="#666666"
> > +     borderopacity="1.0"
> > +     inkscape:pageshadow="2"
> > +     inkscape:pageopacity="0.0"
> > +     inkscape:pagecheckerboard="0"
> > +     showgrid="false"
> > +     inkscape:zoom="1.3311475"
> > +     inkscape:cx="439.84606"
> > +     inkscape:cy="305.37562"
> > +     inkscape:window-width="2400"
> > +     inkscape:window-height="1271"
> > +     inkscape:window-x="2391"
> > +     inkscape:window-y="-9"
> > +     inkscape:window-maximized="1"
> > +     inkscape:current-layer="g101" />
> > +  <defs
> > +     id="defs5">
> > +    <clipPath
> > +       id="clip0">
> > +      <rect
> > +         x="0"
> > +         y="0"
> > +         width="880"
> > +         height="610"
> > +         id="rect2" />
> > +    </clipPath>
> > +  </defs>
> > +  <g
> > +     clip-path="url(#clip0)"
> > +     id="g101">
> > +    <rect
> > +       x="0"
> > +       y="0"
> > +       width="880"
> > +       height="610"
> > +       fill="#FFFFFF"
> > +       id="rect7" />
> > +    <rect
> > +       x="333.5"
> > +       y="0.500053"
> > +       width="234"
> > +       height="45"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#A6A6A6"
> > +       id="rect9" />
> > +    <text
> > +       font-family="Consolas, Consolas_MSFontService, sans-serif"
> > +       font-weight="400"
> > +       font-size="19px"
> > +       transform="translate(357.196,29)"
> > +       id="text11">rte_eth_rx_burst()</text>
> > +    <rect
> > +       x="333.5"
> > +       y="63.5001"
> > +       width="234"
> > +       height="45"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       id="rect13" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(394.666 91)"
> > +       id="text17">analyze <tspan
> > +   font-size="19"
> > +   x="60.9267"
> > +   y="0"
> > +   id="tspan15">packet </tspan></text>
> > +    <rect
> > +       x="572.5"
> > +       y="279.5"
> > +       width="234"
> > +       height="46"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect19" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(591.429 308)"
> > +       id="text21">rte_flow_q_flow_create()</text>
> > +    <path
> > +       d="M333.5 384 450.5 350.5 567.5 384 450.5 417.5Z"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       fill-rule="evenodd"
> > +       id="path23" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(430.069 378)"
> > +       id="text27">more <tspan
> > +   font-size="19"
> > +   x="-12.94"
> > +   y="23"
> > +   id="tspan25">packets?</tspan></text>
> > +    <path
> > +       d="M689.249 325.5 689.249 338.402 450.5 338.402 450.833 338.069
> 450.833 343.971 450.167 343.971 450.167 337.735 688.916 337.735 688.582
> 338.069 688.582 325.5ZM454.5 342.638 450.5 350.638 446.5 342.638Z"
> > +       id="path29" />
> > +    <path
> > +       d="M450.833 45.5 450.833 56.8197 450.167 56.8197 450.167
> 45.5001ZM454.5 55.4864 450.5 63.4864 446.5 55.4864Z"
> > +       id="path31" />
> > +    <path
> > +       d="M450.833 108.5 450.833 120.375 450.167 120.375 450.167
> 108.5ZM454.5 119.041 450.5 127.041 446.5 119.041Z"
> > +       id="path33" />
> > +    <path
> > +       d="M451.833 507.5 451.833 533.61 451.167 533.61 451.167
> 507.5ZM455.5 532.277 451.5 540.277 447.5 532.277Z"
> > +       id="path35" />
> > +    <path
> > +       d="M0 0.333333-23.9993 0.333333-23.666 0-23.666 141.649-23.9993
> 141.316 562.966 141.316 562.633 141.649 562.633 124.315 563.299 124.315
> 563.299 141.983-24.3327 141.983-24.3327-0.333333 0-0.333333ZM558.966
> 125.649 562.966 117.649 566.966 125.649Z"
> > +       transform="matrix(-6.12323e-17 -1 -1 6.12323e-17 451.149 585.466)"
> > +       id="path37" />
> > +    <path
> > +       d="M333.5 160.5 450.5 126.5 567.5 160.5 450.5 194.5Z"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       fill-rule="evenodd"
> > +       id="path39" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(417.576 155)"
> > +       id="text43">add new <tspan
> > +   font-size="19"
> > +   x="13.2867"
> > +   y="23"
> > +   id="tspan41">rule?</tspan></text>
> > +    <path
> > +       d="M567.5 160.167 689.267 160.167 689.267 273.228 688.6 273.228
> 688.6 160.5 688.933 160.833 567.5 160.833ZM692.933 271.894 688.933 279.894
> 684.933 271.894Z"
> > +       id="path45" />
> > +    <rect
> > +       x="602.5"
> > +       y="127.5"
> > +       width="46"
> > +       height="30"
> > +       stroke="#000000"
> > +       stroke-width="0.666667"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       id="rect47" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(611.34 148)"
> > +       id="text49">yes</text>
> > +    <rect
> > +       x="254.5"
> > +       y="126.5"
> > +       width="46"
> > +       height="31"
> > +       stroke="#000000"
> > +       stroke-width="0.666667"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       id="rect51" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(267.182 147)"
> > +       id="text53">no</text>
> > +    <path
> > +       d="M0-0.333333 251.563-0.333333 251.563 298.328 8.00002 298.328
> 8.00002 297.662 251.229 297.662 250.896 297.995 250.896 0 251.229 0.333333 0
> 0.333333ZM9.33333 301.995 1.33333 297.995 9.33333 293.995Z"
> > +       transform="matrix(1 0 0 -1 567.5 383.495)"
> > +       id="path55" />
> > +    <path
> > +       d="M86.5001 213.5 203.5 180.5 320.5 213.5 203.5 246.5Z"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       fill-rule="evenodd"
> > +       id="path57" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(159.155 208)"
> > +       id="text61">destroy the <tspan
> > +   font-size="19"
> > +   x="24.0333"
> > +   y="23"
> > +   id="tspan59">rule?</tspan></text>
> > +    <path
> > +       d="M0-0.333333 131.029-0.333333 131.029 12.9778 130.363 12.9778
> 130.363 0 130.696 0.333333 0 0.333333ZM134.696 11.6445 130.696 19.6445
> 126.696 11.6445Z"
> > +       transform="matrix(-1 1.22465e-16 1.22465e-16 1 334.196 160.5)"
> > +       id="path63" />
> > +    <rect
> > +       x="81.5001"
> > +       y="280.5"
> > +       width="234"
> > +       height="45"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect65" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(96.2282 308)"
> > +       id="text67">rte_flow_q_flow_destroy()</text>
> > +    <path
> > +       d="M0 0.333333-24.0001 0.333333-23.6667 0-23.6667 49.9498-24.0001
> 49.6165 121.748 49.6165 121.748 59.958 121.082 59.958 121.082 49.9498
> 121.415 50.2832-24.3334 50.2832-24.3334-0.333333 0-0.333333ZM125.415
> 58.6247 121.415 66.6247 117.415 58.6247Z"
> > +       transform="matrix(-1 0 0 1 319.915 213.5)"
> > +       id="path69" />
> > +    <path
> > +       d="M86.5001 213.833 62.5002 213.833 62.8335 213.5 62.8335 383.95
> 62.5002 383.617 327.511 383.617 327.511 384.283 62.1668 384.283 62.1668
> 213.167 86.5001 213.167ZM326.178 379.95 334.178 383.95 326.178 387.95Z"
> > +       id="path71" />
> > +    <path
> > +       d="M0-0.333333 12.8273-0.333333 12.8273 252.111 12.494 251.778
> 18.321 251.778 18.321 252.445 12.1607 252.445 12.1607 0 12.494 0.333333 0
> 0.333333ZM16.9877 248.111 24.9877 252.111 16.9877 256.111Z"
> > +       transform="matrix(1.83697e-16 1 1 -1.83697e-16 198.5 325.5)"
> > +       id="path73" />
> > +    <rect
> > +       x="334.5"
> > +       y="540.5"
> > +       width="234"
> > +       height="45"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect75" />
> > +    <text
> > +       font-family="Calibri, Calibri_MSFontService, sans-serif"
> > +       font-weight="400"
> > +       font-size="19px"
> > +       id="text77"
> > +       x="385.08301"
> > +       y="569">rte_flow_q_pull()</text>
> > +    <rect
> > +       x="334.5"
> > +       y="462.5"
> > +       width="234"
> > +       height="45"
> > +       stroke="#000000"
> > +       stroke-width="1.33333"
> > +       stroke-miterlimit="8"
> > +       fill="#FFFFFF"
> > +       id="rect79" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(379.19 491)"
> > +       id="text81">rte_flow_q_push()</text>
> > +    <path
> > +       d="M450.833 417.495 451.402 455.999 450.735 456.008 450.167
> 417.505ZM455.048 454.611 451.167 462.669 447.049 454.729Z"
> > +       id="path83" />
> > +    <rect
> > +       x="0.500053"
> > +       y="287.5"
> > +       width="46"
> > +       height="30"
> > +       stroke="#000000"
> > +       stroke-width="0.666667"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       id="rect85" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(12.8617 308)"
> > +       id="text87">no</text>
> > +    <rect
> > +       x="357.5"
> > +       y="223.5"
> > +       width="47"
> > +       height="31"
> > +       stroke="#000000"
> > +       stroke-width="0.666667"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       id="rect89" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(367.001 244)"
> > +       id="text91">yes</text>
> > +    <rect
> > +       x="469.5"
> > +       y="421.5"
> > +       width="46"
> > +       height="30"
> > +       stroke="#000000"
> > +       stroke-width="0.666667"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       id="rect93" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(481.872 442)"
> > +       id="text95">no</text>
> > +    <rect
> > +       x="832.5"
> > +       y="223.5"
> > +       width="46"
> > +       height="31"
> > +       stroke="#000000"
> > +       stroke-width="0.666667"
> > +       stroke-miterlimit="8"
> > +       fill="#D9D9D9"
> > +       id="rect97" />
> > +    <text
> > +       font-family="Calibri,Calibri_MSFontService,sans-serif"
> > +       font-weight="400"
> > +       font-size="19"
> > +       transform="translate(841.777 244)"
> > +       id="text99">yes</text>
> > +  </g>
> > +</svg>
> > diff --git a/doc/guides/prog_guide/rte_flow.rst
> b/doc/guides/prog_guide/rte_flow.rst
> > index 5391648833..5d47f3bd21 100644
> > --- a/doc/guides/prog_guide/rte_flow.rst
> > +++ b/doc/guides/prog_guide/rte_flow.rst
> > @@ -3607,12 +3607,16 @@ Expected number of counters or meters in an
> application, for example,
> >   allow PMD to prepare and optimize NIC memory layout in advance.
> >   ``rte_flow_configure()`` must be called before any flow rule is created,
> >   but after an Ethernet device is configured.
> > +It also creates flow queues for asynchronous flow rules operations via
> > +queue-based API, see `Asynchronous operations`_ section.
> >
> >   .. code-block:: c
> >
> >      int
> >      rte_flow_configure(uint16_t port_id,
> >                        const struct rte_flow_port_attr *port_attr,
> > +                     uint16_t nb_queue,
> > +                     const struct rte_flow_queue_attr *queue_attr[],
> >                        struct rte_flow_error *error);
> >
> >   Information about resources that can benefit from pre-allocation can be
> > @@ -3737,7 +3741,7 @@ and pattern and actions templates are created.
> >
> >   .. code-block:: c
> >
> > -	rte_flow_configure(port, *port_attr, *error);
> > +	rte_flow_configure(port, *port_attr, nb_queue, *queue_attr,
> *error);
> 
> * before queue_attr looks strange

Yes, it is a typo.

> >
> >   	struct rte_flow_pattern_template *pattern_templates[0] =
> >   		rte_flow_pattern_template_create(port, &itr, &pattern,
> &error);
> > @@ -3750,6 +3754,167 @@ and pattern and actions templates are created.
> >   				*actions_templates, nb_actions_templates,
> >   				*error);
> >
> > +Asynchronous operations
> > +-----------------------
> > +
> > +Flow rules management can be done via special lockless flow
> management queues.
> > +- Queue operations are asynchronous and not thread-safe.
> > +
> > +- Operations can thus be invoked by the app's datapath,
> > +  packet processing can continue while queue operations are processed by
> NIC.
> > +
> > +- The queue number is configured at initialization stage.
> 
> I read "the queue number" as some number for a specific queue.
> May be "Number of queues is configured..."

No problem.

> > +
> > +- Available operation types: rule creation, rule destruction,
> > +  indirect rule creation, indirect rule destruction, indirect rule update.
> > +
> > +- Operations may be reordered within a queue.
> 
> Do we want to have barriers?
> E.g. create rule, destroy the same rule -> reoder -> destroy fails, rule
> lives forever.

API design is crafter with the throughput as the main goal in mind.
We allow user to enforce any ordering outside these functions.
Another point that not all PMDs/NIC will have this out-of-order execution.


> > +
> > +- Operations can be postponed and pushed to NIC in batches.
> > +
> > +- Results pulling must be done on time to avoid queue overflows.
> 
> polling? (as libc poll() which checks status of file descriptors)
> it is not pulling the door to open it :)

poll waits for some event on a file descriptor as it title says.
And then user has to invoke read() to actually get any info from the fd.
The point of our function is to return the result immediately, thus pulling.
We had many names appearing in the thread for these functions.
As we know, naming variables is the second hardest thing in programming.
I wanted this pull for results pulling be a counterpart for the push for
pushing the operations to a NIC. Another idea is pop/push pair, but they are
more like for operations only, not for results.
Having said that I'm at the point of accepting any name here.

> > +
> > +- User data is returned as part of the result to identify an operation.
> > +
> > +- Flow handle is valid once the creation operation is enqueued and must
> be
> > +  destroyed even if the operation is not successful and the rule is not
> inserted.
> > +
> > +The asynchronous flow rule insertion logic can be broken into two phases.
> > +
> > +1. Initialization stage as shown here:
> > +
> > +.. _figure_rte_flow_q_init:
> > +
> > +.. figure:: img/rte_flow_q_init.*
> > +
> > +2. Main loop as presented on a datapath application example:
> > +
> > +.. _figure_rte_flow_q_usage:
> > +
> > +.. figure:: img/rte_flow_q_usage.*
> > +
> > +Enqueue creation operation
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Enqueueing a flow rule creation operation is similar to simple creation.
> > +
> > +.. code-block:: c
> > +
> > +	struct rte_flow *
> > +	rte_flow_q_flow_create(uint16_t port_id,
> > +				uint32_t queue_id,
> > +				const struct rte_flow_q_ops_attr
> *q_ops_attr,
> > +				struct rte_flow_template_table
> *template_table,
> > +				const struct rte_flow_item pattern[],
> > +				uint8_t pattern_template_index,
> > +				const struct rte_flow_action actions[],
> > +				uint8_t actions_template_index,
> > +				struct rte_flow_error *error);
> > +
> > +A valid handle in case of success is returned. It must be destroyed later
> > +by calling ``rte_flow_q_flow_destroy()`` even if the rule is rejected by
> HW.
> > +
> > +Enqueue destruction operation
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Enqueueing a flow rule destruction operation is similar to simple
> destruction.
> > +
> > +.. code-block:: c
> > +
> > +	int
> > +	rte_flow_q_flow_destroy(uint16_t port_id,
> > +				uint32_t queue_id,
> > +				const struct rte_flow_q_ops_attr
> *q_ops_attr,
> > +				struct rte_flow *flow,
> > +				struct rte_flow_error *error);
> > +
> > +Push enqueued operations
> > +~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Pushing all internally stored rules from a queue to the NIC.
> > +
> > +.. code-block:: c
> > +
> > +	int
> > +	rte_flow_q_push(uint16_t port_id,
> > +			uint32_t queue_id,
> > +			struct rte_flow_error *error);
> > +
> > +There is the postpone attribute in the queue operation attributes.
> > +When it is set, multiple operations can be bulked together and not sent to
> HW
> > +right away to save SW/HW interactions and prioritize throughput over
> latency.
> > +The application must invoke this function to actually push all outstanding
> > +operations to HW in this case.
> > +
> > +Pull enqueued operations
> > +~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Pulling asynchronous operations results.
> > +
> > +The application must invoke this function in order to complete
> asynchronous
> > +flow rule operations and to receive flow rule operations statuses.
> > +
> > +.. code-block:: c
> > +
> > +	int
> > +	rte_flow_q_pull(uint16_t port_id,
> > +			uint32_t queue_id,
> > +			struct rte_flow_q_op_res res[],
> > +			uint16_t n_res,
> > +			struct rte_flow_error *error);
> > +
> > +Multiple outstanding operation results can be pulled simultaneously.
> > +User data may be provided during a flow creation/destruction in order
> > +to distinguish between multiple operations. User data is returned as part
> > +of the result to provide a method to detect which operation is completed.
> > +
> > +Enqueue indirect action creation operation
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Asynchronous version of indirect action creation API.
> > +
> > +.. code-block:: c
> > +
> > +	struct rte_flow_action_handle *
> > +	rte_flow_q_action_handle_create(uint16_t port_id,
> > +			uint32_t queue_id,
> > +			const struct rte_flow_q_ops_attr *q_ops_attr,
> > +			const struct rte_flow_indir_action_conf
> *indir_action_conf,
> > +			const struct rte_flow_action *action,
> > +			struct rte_flow_error *error);
> > +
> > +A valid handle in case of success is returned. It must be destroyed later by
> > +calling ``rte_flow_q_action_handle_destroy()`` even if the rule is
> rejected.
> > +
> > +Enqueue indirect action destruction operation
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Asynchronous version of indirect action destruction API.
> > +
> > +.. code-block:: c
> > +
> > +	int
> > +	rte_flow_q_action_handle_destroy(uint16_t port_id,
> > +			uint32_t queue_id,
> > +			const struct rte_flow_q_ops_attr *q_ops_attr,
> > +			struct rte_flow_action_handle *action_handle,
> > +			struct rte_flow_error *error);
> > +
> > +Enqueue indirect action update operation
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > +
> > +Asynchronous version of indirect action update API.
> > +
> > +.. code-block:: c
> > +
> > +	int
> > +	rte_flow_q_action_handle_update(uint16_t port_id,
> > +			uint32_t queue_id,
> > +			const struct rte_flow_q_ops_attr *q_ops_attr,
> > +			struct rte_flow_action_handle *action_handle,
> > +			const void *update,
> > +			struct rte_flow_error *error);
> > +
> >   .. _flow_isolated_mode:
> >
> >   Flow isolated mode
> > diff --git a/doc/guides/rel_notes/release_22_03.rst
> b/doc/guides/rel_notes/release_22_03.rst
> > index 6656b35295..87cea8a966 100644
> > --- a/doc/guides/rel_notes/release_22_03.rst
> > +++ b/doc/guides/rel_notes/release_22_03.rst
> > @@ -83,6 +83,14 @@ New Features
> >       ``rte_flow_template_table_destroy``,
> ``rte_flow_pattern_template_destroy``
> >       and ``rte_flow_actions_template_destroy``.
> >
> > +  * ethdev: Added ``rte_flow_q_flow_create`` and
> ``rte_flow_q_flow_destroy``
> > +    API to enqueue flow creaion/destruction operations asynchronously as
> well
> > +    as ``rte_flow_q_pull`` to poll and retrieve results of these operations
> > +    and ``rte_flow_q_push`` to push all the in-flight operations to the NIC.
> > +    Introduced asynchronous API for indirect actions management as well:
> > +    ``rte_flow_q_action_handle_create``,
> ``rte_flow_q_action_handle_destroy``
> > +    and ``rte_flow_q_action_handle_update``.
> > +
> >   * **Updated AF_XDP PMD**
> >
> >     * Added support for libxdp >=v1.2.2.
> > diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> > index b53f8c9b89..aca5bac2da 100644
> > --- a/lib/ethdev/rte_flow.c
> > +++ b/lib/ethdev/rte_flow.c
> > @@ -1415,6 +1415,8 @@ rte_flow_info_get(uint16_t port_id,
> >   int
> >   rte_flow_configure(uint16_t port_id,
> >   		   const struct rte_flow_port_attr *port_attr,
> > +		   uint16_t nb_queue,
> > +		   const struct rte_flow_queue_attr *queue_attr[],
> >   		   struct rte_flow_error *error)
> >   {
> >   	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > @@ -1424,7 +1426,8 @@ rte_flow_configure(uint16_t port_id,
> >   		return -rte_errno;
> >   	if (likely(!!ops->configure)) {
> >   		return flow_err(port_id,
> > -				ops->configure(dev, port_attr, error),
> > +				ops->configure(dev, port_attr,
> > +					       nb_queue, queue_attr, error),
> >   				error);
> >   	}
> >   	return rte_flow_error_set(error, ENOTSUP,
> > @@ -1578,3 +1581,173 @@ rte_flow_template_table_destroy(uint16_t
> port_id,
> >   				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> >   				  NULL, rte_strerror(ENOTSUP));
> >   }
> > +
> > +struct rte_flow *
> > +rte_flow_q_flow_create(uint16_t port_id,
> > +		       uint32_t queue_id,
> > +		       const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		       struct rte_flow_template_table *template_table,
> > +		       const struct rte_flow_item pattern[],
> > +		       uint8_t pattern_template_index,
> > +		       const struct rte_flow_action actions[],
> > +		       uint8_t actions_template_index,
> > +		       struct rte_flow_error *error)
> > +{
> > +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +	struct rte_flow *flow;
> > +
> > +	if (unlikely(!ops))
> > +		return NULL;
> > +	if (likely(!!ops->q_flow_create)) {
> > +		flow = ops->q_flow_create(dev, queue_id,
> > +					  q_ops_attr, template_table,
> > +					  pattern, pattern_template_index,
> > +					  actions, actions_template_index,
> > +					  error);
> > +		if (flow == NULL)
> > +			flow_err(port_id, -rte_errno, error);
> > +		return flow;
> > +	}
> > +	rte_flow_error_set(error, ENOTSUP,
> > +			   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> > +			   NULL, rte_strerror(ENOTSUP));
> > +	return NULL;
> > +}
> > +
> > +int
> > +rte_flow_q_flow_destroy(uint16_t port_id,
> > +			uint32_t queue_id,
> > +			const struct rte_flow_q_ops_attr *q_ops_attr,
> > +			struct rte_flow *flow,
> > +			struct rte_flow_error *error)
> > +{
> > +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +
> > +	if (unlikely(!ops))
> > +		return -rte_errno;
> > +	if (likely(!!ops->q_flow_destroy)) {
> > +		return flow_err(port_id,
> > +				ops->q_flow_destroy(dev, queue_id,
> > +						    q_ops_attr, flow, error),
> > +				error);
> > +	}
> > +	return rte_flow_error_set(error, ENOTSUP,
> > +				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> > +				  NULL, rte_strerror(ENOTSUP));
> > +}
> > +
> > +struct rte_flow_action_handle *
> > +rte_flow_q_action_handle_create(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		const struct rte_flow_indir_action_conf *indir_action_conf,
> > +		const struct rte_flow_action *action,
> > +		struct rte_flow_error *error)
> > +{
> > +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +	struct rte_flow_action_handle *handle;
> > +
> > +	if (unlikely(!ops))
> > +		return NULL;
> > +	if (unlikely(!ops->q_action_handle_create)) {
> > +		rte_flow_error_set(error, ENOSYS,
> > +				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> NULL,
> > +				   rte_strerror(ENOSYS));
> > +		return NULL;
> > +	}
> > +	handle = ops->q_action_handle_create(dev, queue_id, q_ops_attr,
> > +					     indir_action_conf, action, error);
> > +	if (handle == NULL)
> > +		flow_err(port_id, -rte_errno, error);
> > +	return handle;
> > +}
> > +
> > +int
> > +rte_flow_q_action_handle_destroy(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		struct rte_flow_action_handle *action_handle,
> > +		struct rte_flow_error *error)
> > +{
> > +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +	int ret;
> > +
> > +	if (unlikely(!ops))
> > +		return -rte_errno;
> > +	if (unlikely(!ops->q_action_handle_destroy))
> > +		return rte_flow_error_set(error, ENOSYS,
> > +
> RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> > +					  NULL, rte_strerror(ENOSYS));
> > +	ret = ops->q_action_handle_destroy(dev, queue_id, q_ops_attr,
> > +					   action_handle, error);
> > +	return flow_err(port_id, ret, error);
> > +}
> > +
> > +int
> > +rte_flow_q_action_handle_update(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		struct rte_flow_action_handle *action_handle,
> > +		const void *update,
> > +		struct rte_flow_error *error)
> > +{
> > +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +	int ret;
> > +
> > +	if (unlikely(!ops))
> > +		return -rte_errno;
> > +	if (unlikely(!ops->q_action_handle_update))
> > +		return rte_flow_error_set(error, ENOSYS,
> > +
> RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> > +					  NULL, rte_strerror(ENOSYS));
> > +	ret = ops->q_action_handle_update(dev, queue_id, q_ops_attr,
> > +					  action_handle, update, error);
> > +	return flow_err(port_id, ret, error);
> > +}
> > +
> > +int
> > +rte_flow_q_push(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		struct rte_flow_error *error)
> > +{
> > +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +
> > +	if (unlikely(!ops))
> > +		return -rte_errno;
> > +	if (likely(!!ops->q_push)) {
> > +		return flow_err(port_id,
> > +				ops->q_push(dev, queue_id, error),
> > +				error);
> > +	}
> > +	return rte_flow_error_set(error, ENOTSUP,
> > +				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> > +				  NULL, rte_strerror(ENOTSUP));
> > +}
> > +
> > +int
> > +rte_flow_q_pull(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		struct rte_flow_q_op_res res[],
> > +		uint16_t n_res,
> > +		struct rte_flow_error *error)
> > +{
> > +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> > +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +	int ret;
> > +
> > +	if (unlikely(!ops))
> > +		return -rte_errno;
> > +	if (likely(!!ops->q_pull)) {
> > +		ret = ops->q_pull(dev, queue_id, res, n_res, error);
> > +		return ret ? ret : flow_err(port_id, ret, error);
> > +	}
> > +	return rte_flow_error_set(error, ENOTSUP,
> > +				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
> > +				  NULL, rte_strerror(ENOTSUP));
> > +}
> > diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> > index e87db5a540..b0d4f33bfd 100644
> > --- a/lib/ethdev/rte_flow.h
> > +++ b/lib/ethdev/rte_flow.h
> > @@ -4862,6 +4862,10 @@ rte_flow_flex_item_release(uint16_t port_id,
> >    *
> >    */
> >   struct rte_flow_port_info {
> > +	/**
> > +	 * Number of queues for asynchronous operations.
> 
> Is it a maximum number of queues?

Yes, it is a maximum supported number of flow queues. Will rename.

> 
> > +	 */
> > +	uint32_t nb_queues;
> >   	/**
> >   	 * Number of pre-configurable counter actions.
> >   	 * @see RTE_FLOW_ACTION_TYPE_COUNT
> > @@ -4879,6 +4883,17 @@ struct rte_flow_port_info {
> >   	uint32_t nb_meters;
> >   };
> >
> > +/**
> > + * Flow engine queue configuration.
> > + */
> > +__extension__
> > +struct rte_flow_queue_attr {
> > +	/**
> > +	 * Number of flow rule operations a queue can hold.
> > +	 */
> > +	uint32_t size;
> 
> Whar are the min/max sizes? 0 as the default size, if yes, do we need
> an API to find actual size?

Good catch, will extend rte_flow_info_get() to obtain this number.

> 
> > +};
> > +
> >   /**
> >    * @warning
> >    * @b EXPERIMENTAL: this API may change without prior notice.
> > @@ -4948,6 +4963,11 @@ struct rte_flow_port_attr {
> >    *   Port identifier of Ethernet device.
> >    * @param[in] port_attr
> >    *   Port configuration attributes.
> > + * @param[in] nb_queue
> > + *   Number of flow queues to be configured.
> > + * @param[in] queue_attr
> > + *   Array that holds attributes for each flow queue.
> > + *   Number of elements is set in @p port_attr.nb_queues.
> >    * @param[out] error
> >    *   Perform verbose error reporting if not NULL.
> >    *   PMDs initialize this structure in case of error only.
> > @@ -4959,6 +4979,8 @@ __rte_experimental
> >   int
> >   rte_flow_configure(uint16_t port_id,
> >   		   const struct rte_flow_port_attr *port_attr,
> > +		   uint16_t nb_queue,
> > +		   const struct rte_flow_queue_attr *queue_attr[],
> >   		   struct rte_flow_error *error);
> >
> >   /**
> > @@ -5221,6 +5243,318 @@ rte_flow_template_table_destroy(uint16_t
> port_id,
> >   		struct rte_flow_template_table *template_table,
> >   		struct rte_flow_error *error);
> >
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Queue operation attributes.
> > + */
> > +struct rte_flow_q_ops_attr {
> > +	/**
> > +	 * The user data that will be returned on the completion events.
> > +	 */
> > +	void *user_data;
> 
> IMHO it must not be hiddne in attrs. It is a key information
> which is used to understand the opration result. It should
> be passed separately.

Maybe, on the other hand it is optional and may not be needed by an application.

> > +	 /**
> > +	  * When set, the requested action will not be sent to the HW
> immediately.
> > +	  * The application must call the rte_flow_queue_push to actually
> send it.
> 
> Will the next operation without the attribute set implicitly push it?
> Is it mandatory for the driver to respect it? Or is it just a possible
> optimization hint?

Yes, it will be pushed with all the operations in a queue once the postpone is cleared.
It is not mandatory to respect this bit, PMD can use other optimization technics.

> 
> > +	  */
> > +	uint32_t postpone:1;
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue rule creation operation.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue used to insert the rule.
> > + * @param[in] q_ops_attr
> > + *   Rule creation operation attributes.
> > + * @param[in] template_table
> > + *   Template table to select templates from.
> 
> IMHO it should be done optional. I.e. NULL allows.
> If NULL, indecies are ignored and pattern+actions are full
> specificiation as in rte_flow_create(). The only missing bit
> is attributes.
> Basically I'm sure that hardwiring queue-based flow rule control
> to template is the right solution. It should be possible without
> templates. May be it should be a separate API to be added later
> if/when required.

That ruins the whole point of templates - to use pre-existing hardware paths.
But I agree less performant API may be added if need arises.

 
> > + * @param[in] pattern
> > + *   List of pattern items to be used.
> > + *   The list order should match the order in the pattern template.
> > + *   The spec is the only relevant member of the item that is being used.
> > + * @param[in] pattern_template_index
> > + *   Pattern template index in the table.
> > + * @param[in] actions
> > + *   List of actions to be used.
> > + *   The list order should match the order in the actions template.
> > + * @param[in] actions_template_index
> > + *   Actions template index in the table.
> > + * @param[out] error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   Handle on success, NULL otherwise and rte_errno is set.
> > + *   The rule handle doesn't mean that the rule was offloaded.
> 
> "was offloaded" sounds ambiguous. API says nothing about any kind
> of offloading before. "has been populated" or "has been
> created" (since API says "create").

Ok.


> > + *   Only completion result indicates that the rule was offloaded.
> > + */
> > +__rte_experimental
> > +struct rte_flow *
> > +rte_flow_q_flow_create(uint16_t port_id,
> 
> flow_q_flow does not sound like a good nameing, consider:
> rte_flow_q_rule_create() is <subsystem>_<subtype>_<object>_<action>

More like:
<subsystem>_<subtype>_<object>_<action>
 <rte>_<flow>_<rule_create_operation>_<queue>
Which is pretty lengthy name as for me.


> > +		       uint32_t queue_id,
> > +		       const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		       struct rte_flow_template_table *template_table,
> > +		       const struct rte_flow_item pattern[],
> > +		       uint8_t pattern_template_index,
> > +		       const struct rte_flow_action actions[],
> > +		       uint8_t actions_template_index,
> > +		       struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue rule destruction operation.
> > + *
> > + * This function enqueues a destruction operation on the queue.
> > + * Application should assume that after calling this function
> > + * the rule handle is not valid anymore.
> > + * Completion indicates the full removal of the rule from the HW.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue which is used to destroy the rule.
> > + *   This must match the queue on which the rule was created.
> > + * @param[in] q_ops_attr
> > + *   Rule destroy operation attributes.
> > + * @param[in] flow
> > + *   Flow handle to be destroyed.
> > + * @param[out] error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   0 on success, a negative errno value otherwise and rte_errno is set.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_q_flow_destroy(uint16_t port_id,
> > +			uint32_t queue_id,
> > +			const struct rte_flow_q_ops_attr *q_ops_attr,
> > +			struct rte_flow *flow,
> > +			struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue indirect action creation operation.
> > + * @see rte_flow_action_handle_create
> > + *
> > + * @param[in] port_id
> > + *   Port identifier of Ethernet device.
> > + * @param[in] queue_id
> > + *   Flow queue which is used to create the rule.
> > + * @param[in] q_ops_attr
> > + *   Queue operation attributes.
> > + * @param[in] indir_action_conf
> > + *   Action configuration for the indirect action object creation.
> > + * @param[in] action
> > + *   Specific configuration of the indirect action object.
> > + * @param[out] error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   - (0) if success.
> 
> Hold on. Pointer is returned by the function.

That is an error.

> 
> > + *   - (-ENODEV) if *port_id* invalid.
> > + *   - (-ENOSYS) if underlying device does not support this functionality.
> > + *   - (-EIO) if underlying device is removed.
> > + *   - (-ENOENT) if action pointed by *action* handle was not found.
> > + *   - (-EBUSY) if action pointed by *action* handle still used by some rules
> > + *   rte_errno is also set.
> 
> Which error code should be used if too many ops are enqueued (overflow)?

EAGAIN

> 
> > + */
> > +__rte_experimental
> > +struct rte_flow_action_handle *
> > +rte_flow_q_action_handle_create(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		const struct rte_flow_indir_action_conf *indir_action_conf,
> > +		const struct rte_flow_action *action,
> 
> I don't understand why it differs so much from rule creation.
> Why is action template not used?
> IMHO indirect actions should be dropped from the patch
> and added separately since it is a separate feature.

I agree, they deserve a sperate patch since they are rather resource creations.
But, I'm afraid it is too late for RC1.

> > +		struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue indirect action destruction operation.
> > + * The destroy queue must be the same
> > + * as the queue on which the action was created.
> > + *
> > + * @param[in] port_id
> > + *   Port identifier of Ethernet device.
> > + * @param[in] queue_id
> > + *   Flow queue which is used to destroy the rule.
> > + * @param[in] q_ops_attr
> > + *   Queue operation attributes.
> > + * @param[in] action_handle
> > + *   Handle for the indirect action object to be destroyed.
> > + * @param[out] error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   - (0) if success.
> > + *   - (-ENODEV) if *port_id* invalid.
> > + *   - (-ENOSYS) if underlying device does not support this functionality.
> > + *   - (-EIO) if underlying device is removed.
> > + *   - (-ENOENT) if action pointed by *action* handle was not found.
> > + *   - (-EBUSY) if action pointed by *action* handle still used by some rules
> > + *   rte_errno is also set.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_q_action_handle_destroy(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		struct rte_flow_action_handle *action_handle,
> > +		struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue indirect action update operation.
> > + * @see rte_flow_action_handle_create
> > + *
> > + * @param[in] port_id
> > + *   Port identifier of Ethernet device.
> > + * @param[in] queue_id
> > + *   Flow queue which is used to update the rule.
> > + * @param[in] q_ops_attr
> > + *   Queue operation attributes.
> > + * @param[in] action_handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param[in] update
> > + *   Update profile specification used to modify the action pointed by
> handle.
> > + *   *update* could be with the same type of the immediate action
> corresponding
> > + *   to the *handle* argument when creating, or a wrapper structure
> includes
> > + *   action configuration to be updated and bit fields to indicate the
> member
> > + *   of fields inside the action to update.
> > + * @param[out] error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   - (0) if success.
> > + *   - (-ENODEV) if *port_id* invalid.
> > + *   - (-ENOSYS) if underlying device does not support this functionality.
> > + *   - (-EIO) if underlying device is removed.
> > + *   - (-ENOENT) if action pointed by *action* handle was not found.
> > + *   - (-EBUSY) if action pointed by *action* handle still used by some rules
> > + *   rte_errno is also set.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_q_action_handle_update(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > +		struct rte_flow_action_handle *action_handle,
> > +		const void *update,
> > +		struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Push all internally stored rules to the HW.
> > + * Postponed rules are rules that were inserted with the postpone flag
> set.
> > + * Can be used to notify the HW about batch of rules prepared by the SW
> to
> > + * reduce the number of communications between the HW and SW.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue to be pushed.
> > + * @param[out] error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *    0 on success, a negative errno value otherwise and rte_errno is set.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_q_push(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Queue operation status.
> > + */
> > +enum rte_flow_q_op_status {
> > +	/**
> > +	 * The operation was completed successfully.
> > +	 */
> > +	RTE_FLOW_Q_OP_SUCCESS,
> > +	/**
> > +	 * The operation was not completed successfully.
> > +	 */
> > +	RTE_FLOW_Q_OP_ERROR,
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Queue operation results.
> > + */
> > +__extension__
> > +struct rte_flow_q_op_res {
> > +	/**
> > +	 * Returns the status of the operation that this completion signals.
> > +	 */
> > +	enum rte_flow_q_op_status status;
> > +	/**
> > +	 * The user data that will be returned on the completion events.
> > +	 */
> > +	void *user_data;
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Pull a rte flow operation.
> > + * The application must invoke this function in order to complete
> > + * the flow rule offloading and to retrieve the flow rule operation status.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue which is used to pull the operation.
> > + * @param[out] res
> > + *   Array of results that will be set.
> > + * @param[in] n_res
> > + *   Maximum number of results that can be returned.
> > + *   This value is equal to the size of the res array.
> > + * @param[out] error
> > + *   Perform verbose error reporting if not NULL.
> > + *   PMDs initialize this structure in case of error only.
> > + *
> > + * @return
> > + *   Number of results that were pulled,
> > + *   a negative errno value otherwise and rte_errno is set.
> 
> Don't we want to define negative error code meaning?

They are all standard, don't think we need another copy-paste here.

> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_q_pull(uint16_t port_id,
> > +		uint32_t queue_id,
> > +		struct rte_flow_q_op_res res[],
> > +		uint16_t n_res,
> > +		struct rte_flow_error *error);
> > +
> >   #ifdef __cplusplus
> >   }
> >   #endif
  
Thomas Monjalon Feb. 12, 2022, 9:25 a.m. UTC | #3
12/02/2022 03:19, Alexander Kozyrev:
> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> > On 2/11/22 05:26, Alexander Kozyrev wrote:
> > > +__rte_experimental
> > > +struct rte_flow *
> > > +rte_flow_q_flow_create(uint16_t port_id,
> > 
> > flow_q_flow does not sound like a good nameing, consider:
> > rte_flow_q_rule_create() is <subsystem>_<subtype>_<object>_<action>
> 
> More like:
> <subsystem>_<subtype>_<object>_<action>
>  <rte>_<flow>_<rule_create_operation>_<queue>
> Which is pretty lengthy name as for me.

Naming :)
This one may be improved I think.
What is the problem with replacing "flow" with "rule"?
Is it the right meaning?

> > > +__rte_experimental
> > > +struct rte_flow_action_handle *
> > > +rte_flow_q_action_handle_create(uint16_t port_id,
> > > +		uint32_t queue_id,
> > > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > > +		const struct rte_flow_indir_action_conf *indir_action_conf,
> > > +		const struct rte_flow_action *action,
> > 
> > I don't understand why it differs so much from rule creation.
> > Why is action template not used?
> > IMHO indirect actions should be dropped from the patch
> > and added separately since it is a separate feature.
> 
> I agree, they deserve a sperate patch since they are rather resource creations.
> But, I'm afraid it is too late for RC1.

I think it could be done for RC2.

> > > +/**
> > > + * @warning
> > > + * @b EXPERIMENTAL: this API may change without prior notice.
> > > + *
> > > + * Pull a rte flow operation.
> > > + * The application must invoke this function in order to complete
> > > + * the flow rule offloading and to retrieve the flow rule operation status.
> > > + *
> > > + * @param port_id
> > > + *   Port identifier of Ethernet device.
> > > + * @param queue_id
> > > + *   Flow queue which is used to pull the operation.
> > > + * @param[out] res
> > > + *   Array of results that will be set.
> > > + * @param[in] n_res
> > > + *   Maximum number of results that can be returned.
> > > + *   This value is equal to the size of the res array.
> > > + * @param[out] error
> > > + *   Perform verbose error reporting if not NULL.
> > > + *   PMDs initialize this structure in case of error only.
> > > + *
> > > + * @return
> > > + *   Number of results that were pulled,
> > > + *   a negative errno value otherwise and rte_errno is set.
> > 
> > Don't we want to define negative error code meaning?
> 
> They are all standard, don't think we need another copy-paste here.

That's an API, it needs to be all explicit.
I missed it before, we should add the error codes here.
  
Andrew Rybchenko Feb. 16, 2022, 1:34 p.m. UTC | #4
On 2/12/22 05:19, Alexander Kozyrev wrote:
> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
>> On 2/11/22 05:26, Alexander Kozyrev wrote:
>>> A new, faster, queue-based flow rules management mechanism is needed
>> for
>>> applications offloading rules inside the datapath. This asynchronous
>>> and lockless mechanism frees the CPU for further packet processing and
>>> reduces the performance impact of the flow rules creation/destruction
>>> on the datapath. Note that queues are not thread-safe and the queue
>>> should be accessed from the same thread for all queue operations.
>>> It is the responsibility of the app to sync the queue functions in case
>>> of multi-threaded access to the same queue.
>>>
>>> The rte_flow_q_flow_create() function enqueues a flow creation to the
>>> requested queue. It benefits from already configured resources and sets
>>> unique values on top of item and action templates. A flow rule is enqueued
>>> on the specified flow queue and offloaded asynchronously to the
>> hardware.
>>> The function returns immediately to spare CPU for further packet
>>> processing. The application must invoke the rte_flow_q_pull() function
>>> to complete the flow rule operation offloading, to clear the queue, and to
>>> receive the operation status. The rte_flow_q_flow_destroy() function
>>> enqueues a flow destruction to the requested queue.
>>>
>>> Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
>>> Acked-by: Ori Kam <orika@nvidia.com>

[snip]

>>> +
>>> +- Available operation types: rule creation, rule destruction,
>>> +  indirect rule creation, indirect rule destruction, indirect rule update.
>>> +
>>> +- Operations may be reordered within a queue.
>>
>> Do we want to have barriers?
>> E.g. create rule, destroy the same rule -> reoder -> destroy fails, rule
>> lives forever.
> 
> API design is crafter with the throughput as the main goal in mind.
> We allow user to enforce any ordering outside these functions.
> Another point that not all PMDs/NIC will have this out-of-order execution.

Throughput is nice, but there more important requirements
which must be satistied before talking about performance.
Could you explain me what I should do based on which
information from NIC in order to solve above problem?

>>> +
>>> +- Operations can be postponed and pushed to NIC in batches.
>>> +
>>> +- Results pulling must be done on time to avoid queue overflows.
>>
>> polling? (as libc poll() which checks status of file descriptors)
>> it is not pulling the door to open it :)
> 
> poll waits for some event on a file descriptor as it title says.
> And then user has to invoke read() to actually get any info from the fd.
> The point of our function is to return the result immediately, thus pulling.
> We had many names appearing in the thread for these functions.
> As we know, naming variables is the second hardest thing in programming.
> I wanted this pull for results pulling be a counterpart for the push for
> pushing the operations to a NIC. Another idea is pop/push pair, but they are
> more like for operations only, not for results.
> Having said that I'm at the point of accepting any name here.

I agree that it is hard to choose good naming.
Just want to say that polling is not alway waiting.

poll - check the status of (a device), especially as part of a repeated 
cycle.

Here we're checking status of flow engine requests and yes,
finally in a repeated cycle.

[snip]

>>> +/**
>>> + * @warning
>>> + * @b EXPERIMENTAL: this API may change without prior notice.
>>> + *
>>> + * Queue operation attributes.
>>> + */
>>> +struct rte_flow_q_ops_attr {
>>> +	/**
>>> +	 * The user data that will be returned on the completion events.
>>> +	 */
>>> +	void *user_data;
>>
>> IMHO it must not be hiddne in attrs. It is a key information
>> which is used to understand the opration result. It should
>> be passed separately.
> 
> Maybe, on the other hand it is optional and may not be needed by an application.

I don't understand how it is possible. Without it application
don't know fate of its requests.

>>> +	 /**
>>> +	  * When set, the requested action will not be sent to the HW
>> immediately.
>>> +	  * The application must call the rte_flow_queue_push to actually
>> send it.
>>
>> Will the next operation without the attribute set implicitly push it?
>> Is it mandatory for the driver to respect it? Or is it just a possible
>> optimization hint?
> 
> Yes, it will be pushed with all the operations in a queue once the postpone is cleared.
> It is not mandatory to respect this bit, PMD can use other optimization technics.

Could you clarify it in the description.

>>
>>> +	  */
>>> +	uint32_t postpone:1;
>>> +};
[snip]
  
Ori Kam Feb. 16, 2022, 2:53 p.m. UTC | #5
Hi Andew,

> -----Original Message-----
> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Sent: Wednesday, February 16, 2022 3:34 PM
> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> 
> On 2/12/22 05:19, Alexander Kozyrev wrote:
> > On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> >> On 2/11/22 05:26, Alexander Kozyrev wrote:
> >>> A new, faster, queue-based flow rules management mechanism is needed
> >> for
> >>> applications offloading rules inside the datapath. This asynchronous
> >>> and lockless mechanism frees the CPU for further packet processing and
> >>> reduces the performance impact of the flow rules creation/destruction
> >>> on the datapath. Note that queues are not thread-safe and the queue
> >>> should be accessed from the same thread for all queue operations.
> >>> It is the responsibility of the app to sync the queue functions in case
> >>> of multi-threaded access to the same queue.
> >>>
> >>> The rte_flow_q_flow_create() function enqueues a flow creation to the
> >>> requested queue. It benefits from already configured resources and sets
> >>> unique values on top of item and action templates. A flow rule is enqueued
> >>> on the specified flow queue and offloaded asynchronously to the
> >> hardware.
> >>> The function returns immediately to spare CPU for further packet
> >>> processing. The application must invoke the rte_flow_q_pull() function
> >>> to complete the flow rule operation offloading, to clear the queue, and to
> >>> receive the operation status. The rte_flow_q_flow_destroy() function
> >>> enqueues a flow destruction to the requested queue.
> >>>
> >>> Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
> >>> Acked-by: Ori Kam <orika@nvidia.com>
> 
> [snip]
> 
> >>> +
> >>> +- Available operation types: rule creation, rule destruction,
> >>> +  indirect rule creation, indirect rule destruction, indirect rule update.
> >>> +
> >>> +- Operations may be reordered within a queue.
> >>
> >> Do we want to have barriers?
> >> E.g. create rule, destroy the same rule -> reoder -> destroy fails, rule
> >> lives forever.
> >
> > API design is crafter with the throughput as the main goal in mind.
> > We allow user to enforce any ordering outside these functions.
> > Another point that not all PMDs/NIC will have this out-of-order execution.
> 
> Throughput is nice, but there more important requirements
> which must be satistied before talking about performance.
> Could you explain me what I should do based on which
> information from NIC in order to solve above problem?
> 

The idea is that if application has dependency between the rules/ rules operations.
It should wait for the completion of the operation before sending the dependent operation.
In the example you provided above, according to the documeation application should wait
for the completion of the flow creation before destroying it.

> >>> +
> >>> +- Operations can be postponed and pushed to NIC in batches.
> >>> +
> >>> +- Results pulling must be done on time to avoid queue overflows.
> >>
> >> polling? (as libc poll() which checks status of file descriptors)
> >> it is not pulling the door to open it :)
> >
> > poll waits for some event on a file descriptor as it title says.
> > And then user has to invoke read() to actually get any info from the fd.
> > The point of our function is to return the result immediately, thus pulling.
> > We had many names appearing in the thread for these functions.
> > As we know, naming variables is the second hardest thing in programming.
> > I wanted this pull for results pulling be a counterpart for the push for
> > pushing the operations to a NIC. Another idea is pop/push pair, but they are
> > more like for operations only, not for results.
> > Having said that I'm at the point of accepting any name here.
> 
> I agree that it is hard to choose good naming.
> Just want to say that polling is not alway waiting.
> 
> poll - check the status of (a device), especially as part of a repeated
> cycle.
> 
> Here we're checking status of flow engine requests and yes,
> finally in a repeated cycle.
> 
> [snip]
> 
> >>> +/**
> >>> + * @warning
> >>> + * @b EXPERIMENTAL: this API may change without prior notice.
> >>> + *
> >>> + * Queue operation attributes.
> >>> + */
> >>> +struct rte_flow_q_ops_attr {
> >>> +	/**
> >>> +	 * The user data that will be returned on the completion events.
> >>> +	 */
> >>> +	void *user_data;
> >>
> >> IMHO it must not be hiddne in attrs. It is a key information
> >> which is used to understand the opration result. It should
> >> be passed separately.
> >
> > Maybe, on the other hand it is optional and may not be needed by an application.
> 
> I don't understand how it is possible. Without it application
> don't know fate of its requests.
> 
IMHO since user_data should be in all related operations API
along with the attr, splitting the user_data will just add extra parameter
to each function call. Since we have number of functions and will add
more in future I think it will be best to keep it in this location.

> >>> +	 /**
> >>> +	  * When set, the requested action will not be sent to the HW
> >> immediately.
> >>> +	  * The application must call the rte_flow_queue_push to actually
> >> send it.
> >>
> >> Will the next operation without the attribute set implicitly push it?
> >> Is it mandatory for the driver to respect it? Or is it just a possible
> >> optimization hint?
> >
> > Yes, it will be pushed with all the operations in a queue once the postpone is cleared.
> > It is not mandatory to respect this bit, PMD can use other optimization technics.
> 
> Could you clarify it in the description.
> 
> >>
> >>> +	  */
> >>> +	uint32_t postpone:1;
> >>> +};
> [snip]

Best,
Ori
  
Ori Kam Feb. 16, 2022, 3:15 p.m. UTC | #6
Hi Andew,

I missed on comments PSB,

> -----Original Message-----
> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> 
> On 2/12/22 05:19, Alexander Kozyrev wrote:
> > On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> >> On 2/11/22 05:26, Alexander Kozyrev wrote:
> >>> A new, faster, queue-based flow rules management mechanism is needed

[Snip]


> >>> +
> >>> +- Operations can be postponed and pushed to NIC in batches.
> >>> +
> >>> +- Results pulling must be done on time to avoid queue overflows.
> >>
> >> polling? (as libc poll() which checks status of file descriptors)
> >> it is not pulling the door to open it :)
> >
> > poll waits for some event on a file descriptor as it title says.
> > And then user has to invoke read() to actually get any info from the fd.
> > The point of our function is to return the result immediately, thus pulling.
> > We had many names appearing in the thread for these functions.
> > As we know, naming variables is the second hardest thing in programming.
> > I wanted this pull for results pulling be a counterpart for the push for
> > pushing the operations to a NIC. Another idea is pop/push pair, but they are
> > more like for operations only, not for results.
> > Having said that I'm at the point of accepting any name here.
> 
> I agree that it is hard to choose good naming.
> Just want to say that polling is not alway waiting.
> 
> poll - check the status of (a device), especially as part of a repeated
> cycle.
> 
> Here we're checking status of flow engine requests and yes,
> finally in a repeated cycle.
> 
I think the best name should be dequeue since it means that
the calling app gets back info and also free space in the the qeueue.
My second option is the pull, since again it implies that we are getting back
something from the queue and not just waiting for event.

Best,
Ori
  
Alexander Kozyrev Feb. 16, 2022, 10:49 p.m. UTC | #7
On Sat, Feb 12, 2022 4:25 Thomas Monjalon <thomas@monjalon.net> wrote:
> 12/02/2022 03:19, Alexander Kozyrev:
> > On Fri, Feb 11, 2022 7:42 Andrew Rybchenko
> <andrew.rybchenko@oktetlabs.ru>:
> > > On 2/11/22 05:26, Alexander Kozyrev wrote:
> > > > +__rte_experimental
> > > > +struct rte_flow *
> > > > +rte_flow_q_flow_create(uint16_t port_id,
> > >
> > > flow_q_flow does not sound like a good nameing, consider:
> > > rte_flow_q_rule_create() is
> <subsystem>_<subtype>_<object>_<action>
> >
> > More like:
> > <subsystem>_<subtype>_<object>_<action>
> >  <rte>_<flow>_<rule_create_operation>_<queue>
> > Which is pretty lengthy name as for me.
> 
> Naming :)
> This one may be improved I think.
> What is the problem with replacing "flow" with "rule"?
> Is it the right meaning?

I've got a better naming for all the functions. What do you think about this?
Asynchronous rte_flow_async_create and rte_flow_async_destroy functions
as an extension of synchronous rte_flow_create/ rte_flow_destroy API.
The same is true for asynchronous API for indirect actions:
	rte_flow_async_action_handle_create;
	rte_flow_async_action_handle_destroy;
	rte_flow_async_action_handle_update;
And rte_flow_push/rte_flow_pull without "_q_" part to make them clearer.
And yes, I'm still thinking pull is better than poll since we are actually retrieving
something, not just checking if it has something we can retrieve.
Let me know if we can agree on this scheme? Look pretty close to existing one.

> > > > +__rte_experimental
> > > > +struct rte_flow_action_handle *
> > > > +rte_flow_q_action_handle_create(uint16_t port_id,
> > > > +		uint32_t queue_id,
> > > > +		const struct rte_flow_q_ops_attr *q_ops_attr,
> > > > +		const struct rte_flow_indir_action_conf *indir_action_conf,
> > > > +		const struct rte_flow_action *action,
> > >
> > > I don't understand why it differs so much from rule creation.
> > > Why is action template not used?
> > > IMHO indirect actions should be dropped from the patch
> > > and added separately since it is a separate feature.
> >
> > I agree, they deserve a sperate patch since they are rather resource
> creations.
> > But, I'm afraid it is too late for RC1.
> 
> I think it could be done for RC2.

No problem, I'll create a separate commit for indirect actions.

> 
> > > > +/**
> > > > + * @warning
> > > > + * @b EXPERIMENTAL: this API may change without prior notice.
> > > > + *
> > > > + * Pull a rte flow operation.
> > > > + * The application must invoke this function in order to complete
> > > > + * the flow rule offloading and to retrieve the flow rule operation
> status.
> > > > + *
> > > > + * @param port_id
> > > > + *   Port identifier of Ethernet device.
> > > > + * @param queue_id
> > > > + *   Flow queue which is used to pull the operation.
> > > > + * @param[out] res
> > > > + *   Array of results that will be set.
> > > > + * @param[in] n_res
> > > > + *   Maximum number of results that can be returned.
> > > > + *   This value is equal to the size of the res array.
> > > > + * @param[out] error
> > > > + *   Perform verbose error reporting if not NULL.
> > > > + *   PMDs initialize this structure in case of error only.
> > > > + *
> > > > + * @return
> > > > + *   Number of results that were pulled,
> > > > + *   a negative errno value otherwise and rte_errno is set.
> > >
> > > Don't we want to define negative error code meaning?
> >
> > They are all standard, don't think we need another copy-paste here.
> 
> That's an API, it needs to be all explicit.
> I missed it before, we should add the error codes here.

I'll add if you want to see them listed.
  
Thomas Monjalon Feb. 17, 2022, 8:18 a.m. UTC | #8
16/02/2022 23:49, Alexander Kozyrev:
> On Sat, Feb 12, 2022 4:25 Thomas Monjalon <thomas@monjalon.net> wrote:
> > 12/02/2022 03:19, Alexander Kozyrev:
> > > On Fri, Feb 11, 2022 7:42 Andrew Rybchenko
> > <andrew.rybchenko@oktetlabs.ru>:
> > > > On 2/11/22 05:26, Alexander Kozyrev wrote:
> > > > > +__rte_experimental
> > > > > +struct rte_flow *
> > > > > +rte_flow_q_flow_create(uint16_t port_id,
> > > >
> > > > flow_q_flow does not sound like a good nameing, consider:
> > > > rte_flow_q_rule_create() is
> > <subsystem>_<subtype>_<object>_<action>
> > >
> > > More like:
> > > <subsystem>_<subtype>_<object>_<action>
> > >  <rte>_<flow>_<rule_create_operation>_<queue>
> > > Which is pretty lengthy name as for me.
> > 
> > Naming :)
> > This one may be improved I think.
> > What is the problem with replacing "flow" with "rule"?
> > Is it the right meaning?
> 
> I've got a better naming for all the functions. What do you think about this?
> Asynchronous rte_flow_async_create and rte_flow_async_destroy functions
> as an extension of synchronous rte_flow_create/ rte_flow_destroy API.
> The same is true for asynchronous API for indirect actions:
> 	rte_flow_async_action_handle_create;
> 	rte_flow_async_action_handle_destroy;
> 	rte_flow_async_action_handle_update;
> And rte_flow_push/rte_flow_pull without "_q_" part to make them clearer.
> And yes, I'm still thinking pull is better than poll since we are actually retrieving
> something, not just checking if it has something we can retrieve.
> Let me know if we can agree on this scheme? Look pretty close to existing one.

I like the "async" word.

In summary, you propose this change for the functions of this patch:

	rte_flow_q_flow_create           -> rte_flow_async_create
	rte_flow_q_flow_destroy          -> rte_flow_async_destroy
	rte_flow_q_action_handle_create  -> rte_flow_async_action_handle_create
	rte_flow_q_action_handle_destroy -> rte_flow_async_action_handle_destroy
	rte_flow_q_action_handle_update  -> rte_flow_async_action_handle_update
	rte_flow_q_push                  -> rte_flow_push
	rte_flow_q_pull                  -> rte_flow_pull

They are close to the exisiting synchronous function names:

	rte_flow_create
	rte_flow_destroy
	rte_flow_action_handle_create
	rte_flow_action_handle_destroy
	rte_flow_action_handle_update

I think it is a good naming scheme.
  
Andrew Rybchenko Feb. 17, 2022, 10:52 a.m. UTC | #9
Hi Ori,

On 2/16/22 17:53, Ori Kam wrote:
> Hi Andew,
> 
>> -----Original Message-----
>> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
>> Sent: Wednesday, February 16, 2022 3:34 PM
>> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
>>
>> On 2/12/22 05:19, Alexander Kozyrev wrote:
>>> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
>>>> On 2/11/22 05:26, Alexander Kozyrev wrote:
>>>>> A new, faster, queue-based flow rules management mechanism is needed
>>>> for
>>>>> applications offloading rules inside the datapath. This asynchronous
>>>>> and lockless mechanism frees the CPU for further packet processing and
>>>>> reduces the performance impact of the flow rules creation/destruction
>>>>> on the datapath. Note that queues are not thread-safe and the queue
>>>>> should be accessed from the same thread for all queue operations.
>>>>> It is the responsibility of the app to sync the queue functions in case
>>>>> of multi-threaded access to the same queue.
>>>>>
>>>>> The rte_flow_q_flow_create() function enqueues a flow creation to the
>>>>> requested queue. It benefits from already configured resources and sets
>>>>> unique values on top of item and action templates. A flow rule is enqueued
>>>>> on the specified flow queue and offloaded asynchronously to the
>>>> hardware.
>>>>> The function returns immediately to spare CPU for further packet
>>>>> processing. The application must invoke the rte_flow_q_pull() function
>>>>> to complete the flow rule operation offloading, to clear the queue, and to
>>>>> receive the operation status. The rte_flow_q_flow_destroy() function
>>>>> enqueues a flow destruction to the requested queue.
>>>>>
>>>>> Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
>>>>> Acked-by: Ori Kam <orika@nvidia.com>
>>
>> [snip]
>>
>>>>> +
>>>>> +- Available operation types: rule creation, rule destruction,
>>>>> +  indirect rule creation, indirect rule destruction, indirect rule update.
>>>>> +
>>>>> +- Operations may be reordered within a queue.
>>>>
>>>> Do we want to have barriers?
>>>> E.g. create rule, destroy the same rule -> reoder -> destroy fails, rule
>>>> lives forever.
>>>
>>> API design is crafter with the throughput as the main goal in mind.
>>> We allow user to enforce any ordering outside these functions.
>>> Another point that not all PMDs/NIC will have this out-of-order execution.
>>
>> Throughput is nice, but there more important requirements
>> which must be satistied before talking about performance.
>> Could you explain me what I should do based on which
>> information from NIC in order to solve above problem?
>>
> 
> The idea is that if application has dependency between the rules/ rules operations.
> It should wait for the completion of the operation before sending the dependent operation.
> In the example you provided above, according to the documeation application should wait
> for the completion of the flow creation before destroying it.

I see, thanks. May be I read documentation not that attentive.
I'll reread on the next version review cycle.

>>>>> +
>>>>> +- Operations can be postponed and pushed to NIC in batches.
>>>>> +
>>>>> +- Results pulling must be done on time to avoid queue overflows.
>>>>
>>>> polling? (as libc poll() which checks status of file descriptors)
>>>> it is not pulling the door to open it :)
>>>
>>> poll waits for some event on a file descriptor as it title says.
>>> And then user has to invoke read() to actually get any info from the fd.
>>> The point of our function is to return the result immediately, thus pulling.
>>> We had many names appearing in the thread for these functions.
>>> As we know, naming variables is the second hardest thing in programming.
>>> I wanted this pull for results pulling be a counterpart for the push for
>>> pushing the operations to a NIC. Another idea is pop/push pair, but they are
>>> more like for operations only, not for results.
>>> Having said that I'm at the point of accepting any name here.
>>
>> I agree that it is hard to choose good naming.
>> Just want to say that polling is not alway waiting.
>>
>> poll - check the status of (a device), especially as part of a repeated
>> cycle.
>>
>> Here we're checking status of flow engine requests and yes,
>> finally in a repeated cycle.
>>
>> [snip]
>>
>>>>> +/**
>>>>> + * @warning
>>>>> + * @b EXPERIMENTAL: this API may change without prior notice.
>>>>> + *
>>>>> + * Queue operation attributes.
>>>>> + */
>>>>> +struct rte_flow_q_ops_attr {
>>>>> +	/**
>>>>> +	 * The user data that will be returned on the completion events.
>>>>> +	 */
>>>>> +	void *user_data;
>>>>
>>>> IMHO it must not be hiddne in attrs. It is a key information
>>>> which is used to understand the opration result. It should
>>>> be passed separately.
>>>
>>> Maybe, on the other hand it is optional and may not be needed by an application.
>>
>> I don't understand how it is possible. Without it application
>> don't know fate of its requests.
>>
> IMHO since user_data should be in all related operations API
> along with the attr, splitting the user_data will just add extra parameter
> to each function call. Since we have number of functions and will add
> more in future I think it will be best to keep it in this location.

My problem with hiding user_data inside attr is that
'user_data' is not an auxiliary attribute defining extra
properties of the request. It is a key information.
May be attr is not an ideal name for such grouping
of parameters. Unfortunately I have no better ideas right now.

Andrew.
  
Andrew Rybchenko Feb. 17, 2022, 11:02 a.m. UTC | #10
On 2/17/22 11:18, Thomas Monjalon wrote:
> 16/02/2022 23:49, Alexander Kozyrev:
>> On Sat, Feb 12, 2022 4:25 Thomas Monjalon <thomas@monjalon.net> wrote:
>>> 12/02/2022 03:19, Alexander Kozyrev:
>>>> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko
>>> <andrew.rybchenko@oktetlabs.ru>:
>>>>> On 2/11/22 05:26, Alexander Kozyrev wrote:
>>>>>> +__rte_experimental
>>>>>> +struct rte_flow *
>>>>>> +rte_flow_q_flow_create(uint16_t port_id,
>>>>>
>>>>> flow_q_flow does not sound like a good nameing, consider:
>>>>> rte_flow_q_rule_create() is
>>> <subsystem>_<subtype>_<object>_<action>
>>>>
>>>> More like:
>>>> <subsystem>_<subtype>_<object>_<action>
>>>>   <rte>_<flow>_<rule_create_operation>_<queue>
>>>> Which is pretty lengthy name as for me.
>>>
>>> Naming :)
>>> This one may be improved I think.
>>> What is the problem with replacing "flow" with "rule"?
>>> Is it the right meaning?
>>
>> I've got a better naming for all the functions. What do you think about this?
>> Asynchronous rte_flow_async_create and rte_flow_async_destroy functions
>> as an extension of synchronous rte_flow_create/ rte_flow_destroy API.
>> The same is true for asynchronous API for indirect actions:
>> 	rte_flow_async_action_handle_create;
>> 	rte_flow_async_action_handle_destroy;
>> 	rte_flow_async_action_handle_update;
>> And rte_flow_push/rte_flow_pull without "_q_" part to make them clearer.
>> And yes, I'm still thinking pull is better than poll since we are actually retrieving
>> something, not just checking if it has something we can retrieve.
>> Let me know if we can agree on this scheme? Look pretty close to existing one.
> 
> I like the "async" word.
> 
> In summary, you propose this change for the functions of this patch:
> 
> 	rte_flow_q_flow_create           -> rte_flow_async_create
> 	rte_flow_q_flow_destroy          -> rte_flow_async_destroy
> 	rte_flow_q_action_handle_create  -> rte_flow_async_action_handle_create
> 	rte_flow_q_action_handle_destroy -> rte_flow_async_action_handle_destroy
> 	rte_flow_q_action_handle_update  -> rte_flow_async_action_handle_update
> 	rte_flow_q_push                  -> rte_flow_push
> 	rte_flow_q_pull                  -> rte_flow_pull
> 
> They are close to the exisiting synchronous function names:
> 
> 	rte_flow_create
> 	rte_flow_destroy
> 	rte_flow_action_handle_create
> 	rte_flow_action_handle_destroy
> 	rte_flow_action_handle_update
> 
> I think it is a good naming scheme.

+1
  
Ori Kam Feb. 17, 2022, 11:08 a.m. UTC | #11
Hi Andrew,

> -----Original Message-----
> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Sent: Thursday, February 17, 2022 12:53 PM
> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> 
> Hi Ori,
> 
> On 2/16/22 17:53, Ori Kam wrote:
> > Hi Andew,
> >
> >> -----Original Message-----
> >> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> >> Sent: Wednesday, February 16, 2022 3:34 PM
> >> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> >>
> >> On 2/12/22 05:19, Alexander Kozyrev wrote:
> >>> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> >>>> On 2/11/22 05:26, Alexander Kozyrev wrote:
> >>>>> A new, faster, queue-based flow rules management mechanism is needed
> >>>> for
> >>>>> applications offloading rules inside the datapath. This asynchronous
> >>>>> and lockless mechanism frees the CPU for further packet processing and
> >>>>> reduces the performance impact of the flow rules creation/destruction
> >>>>> on the datapath. Note that queues are not thread-safe and the queue
> >>>>> should be accessed from the same thread for all queue operations.
> >>>>> It is the responsibility of the app to sync the queue functions in case
> >>>>> of multi-threaded access to the same queue.
> >>>>>
> >>>>> The rte_flow_q_flow_create() function enqueues a flow creation to the
> >>>>> requested queue. It benefits from already configured resources and sets
> >>>>> unique values on top of item and action templates. A flow rule is enqueued
> >>>>> on the specified flow queue and offloaded asynchronously to the
> >>>> hardware.
> >>>>> The function returns immediately to spare CPU for further packet
> >>>>> processing. The application must invoke the rte_flow_q_pull() function
> >>>>> to complete the flow rule operation offloading, to clear the queue, and to
> >>>>> receive the operation status. The rte_flow_q_flow_destroy() function
> >>>>> enqueues a flow destruction to the requested queue.
> >>>>>
> >>>>> Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
> >>>>> Acked-by: Ori Kam <orika@nvidia.com>
> >>
> >> [snip]
> >>
> >>>>> +
> >>>>> +- Available operation types: rule creation, rule destruction,
> >>>>> +  indirect rule creation, indirect rule destruction, indirect rule update.
> >>>>> +
> >>>>> +- Operations may be reordered within a queue.
> >>>>
> >>>> Do we want to have barriers?
> >>>> E.g. create rule, destroy the same rule -> reoder -> destroy fails, rule
> >>>> lives forever.
> >>>
> >>> API design is crafter with the throughput as the main goal in mind.
> >>> We allow user to enforce any ordering outside these functions.
> >>> Another point that not all PMDs/NIC will have this out-of-order execution.
> >>
> >> Throughput is nice, but there more important requirements
> >> which must be satistied before talking about performance.
> >> Could you explain me what I should do based on which
> >> information from NIC in order to solve above problem?
> >>
> >
> > The idea is that if application has dependency between the rules/ rules operations.
> > It should wait for the completion of the operation before sending the dependent operation.
> > In the example you provided above, according to the documeation application should wait
> > for the completion of the flow creation before destroying it.
> 
> I see, thanks. May be I read documentation not that attentive.
> I'll reread on the next version review cycle.
> 
> >>>>> +
> >>>>> +- Operations can be postponed and pushed to NIC in batches.
> >>>>> +
> >>>>> +- Results pulling must be done on time to avoid queue overflows.
> >>>>
> >>>> polling? (as libc poll() which checks status of file descriptors)
> >>>> it is not pulling the door to open it :)
> >>>
> >>> poll waits for some event on a file descriptor as it title says.
> >>> And then user has to invoke read() to actually get any info from the fd.
> >>> The point of our function is to return the result immediately, thus pulling.
> >>> We had many names appearing in the thread for these functions.
> >>> As we know, naming variables is the second hardest thing in programming.
> >>> I wanted this pull for results pulling be a counterpart for the push for
> >>> pushing the operations to a NIC. Another idea is pop/push pair, but they are
> >>> more like for operations only, not for results.
> >>> Having said that I'm at the point of accepting any name here.
> >>
> >> I agree that it is hard to choose good naming.
> >> Just want to say that polling is not alway waiting.
> >>
> >> poll - check the status of (a device), especially as part of a repeated
> >> cycle.
> >>
> >> Here we're checking status of flow engine requests and yes,
> >> finally in a repeated cycle.
> >>
> >> [snip]
> >>
> >>>>> +/**
> >>>>> + * @warning
> >>>>> + * @b EXPERIMENTAL: this API may change without prior notice.
> >>>>> + *
> >>>>> + * Queue operation attributes.
> >>>>> + */
> >>>>> +struct rte_flow_q_ops_attr {
> >>>>> +	/**
> >>>>> +	 * The user data that will be returned on the completion events.
> >>>>> +	 */
> >>>>> +	void *user_data;
> >>>>
> >>>> IMHO it must not be hiddne in attrs. It is a key information
> >>>> which is used to understand the opration result. It should
> >>>> be passed separately.
> >>>
> >>> Maybe, on the other hand it is optional and may not be needed by an application.
> >>
> >> I don't understand how it is possible. Without it application
> >> don't know fate of its requests.
> >>
> > IMHO since user_data should be in all related operations API
> > along with the attr, splitting the user_data will just add extra parameter
> > to each function call. Since we have number of functions and will add
> > more in future I think it will be best to keep it in this location.
> 
> My problem with hiding user_data inside attr is that
> 'user_data' is not an auxiliary attribute defining extra
> properties of the request. It is a key information.
> May be attr is not an ideal name for such grouping
> of parameters. Unfortunately I have no better ideas right now.
> 
I understand your point, if you don't have objections lets keep the current one
and if needed we will modify.
Is that O.K?

> Andrew.
  
Andrew Rybchenko Feb. 17, 2022, 11:10 a.m. UTC | #12
On 2/16/22 18:15, Ori Kam wrote:
> Hi Andew,
> 
> I missed on comments PSB,
> 
>> -----Original Message-----
>> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
>> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
>>
>> On 2/12/22 05:19, Alexander Kozyrev wrote:
>>> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
>>>> On 2/11/22 05:26, Alexander Kozyrev wrote:
>>>>> A new, faster, queue-based flow rules management mechanism is needed
> 
> [Snip]
> 
> 
>>>>> +
>>>>> +- Operations can be postponed and pushed to NIC in batches.
>>>>> +
>>>>> +- Results pulling must be done on time to avoid queue overflows.
>>>>
>>>> polling? (as libc poll() which checks status of file descriptors)
>>>> it is not pulling the door to open it :)
>>>
>>> poll waits for some event on a file descriptor as it title says.
>>> And then user has to invoke read() to actually get any info from the fd.
>>> The point of our function is to return the result immediately, thus pulling.
>>> We had many names appearing in the thread for these functions.
>>> As we know, naming variables is the second hardest thing in programming.
>>> I wanted this pull for results pulling be a counterpart for the push for
>>> pushing the operations to a NIC. Another idea is pop/push pair, but they are
>>> more like for operations only, not for results.
>>> Having said that I'm at the point of accepting any name here.
>>
>> I agree that it is hard to choose good naming.
>> Just want to say that polling is not alway waiting.
>>
>> poll - check the status of (a device), especially as part of a repeated
>> cycle.
>>
>> Here we're checking status of flow engine requests and yes,
>> finally in a repeated cycle.
>>
> I think the best name should be dequeue since it means that
> the calling app gets back info and also free space in the the qeueue.

Dequeue is bad since it is not a queue because of out-of-order
completions. So, if it is a ring, completion of one request
does not always free space in ring. May be it should not be
treated as a ring.

> My second option is the pull, since again it implies that we are getting back
> something from the queue and not just waiting for event.

I'll think a bit more about it.
  
Ori Kam Feb. 17, 2022, 11:19 a.m. UTC | #13
Hi Andrew,

> -----Original Message-----
> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Sent: Thursday, February 17, 2022 1:11 PM
> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> 
> On 2/16/22 18:15, Ori Kam wrote:
> > Hi Andew,
> >
> > I missed on comments PSB,
> >
> >> -----Original Message-----
> >> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> >> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> >>
> >> On 2/12/22 05:19, Alexander Kozyrev wrote:
> >>> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> >>>> On 2/11/22 05:26, Alexander Kozyrev wrote:
> >>>>> A new, faster, queue-based flow rules management mechanism is needed
> >
> > [Snip]
> >
> >
> >>>>> +
> >>>>> +- Operations can be postponed and pushed to NIC in batches.
> >>>>> +
> >>>>> +- Results pulling must be done on time to avoid queue overflows.
> >>>>
> >>>> polling? (as libc poll() which checks status of file descriptors)
> >>>> it is not pulling the door to open it :)
> >>>
> >>> poll waits for some event on a file descriptor as it title says.
> >>> And then user has to invoke read() to actually get any info from the fd.
> >>> The point of our function is to return the result immediately, thus pulling.
> >>> We had many names appearing in the thread for these functions.
> >>> As we know, naming variables is the second hardest thing in programming.
> >>> I wanted this pull for results pulling be a counterpart for the push for
> >>> pushing the operations to a NIC. Another idea is pop/push pair, but they are
> >>> more like for operations only, not for results.
> >>> Having said that I'm at the point of accepting any name here.
> >>
> >> I agree that it is hard to choose good naming.
> >> Just want to say that polling is not alway waiting.
> >>
> >> poll - check the status of (a device), especially as part of a repeated
> >> cycle.
> >>
> >> Here we're checking status of flow engine requests and yes,
> >> finally in a repeated cycle.
> >>
> > I think the best name should be dequeue since it means that
> > the calling app gets back info and also free space in the the qeueue.
> 
> Dequeue is bad since it is not a queue because of out-of-order
> completions. So, if it is a ring, completion of one request
> does not always free space in ring. May be it should not be
> treated as a ring.
> 
Like I said I'm O.K with the pull version.
I had many many descussions about the queue, I was also thinking about it the
way you do, that saying queue means it is ordered, but that is not true by definition
you can have unordered queue. (for example priority queues) or just
your everyday queue in the store that at the end will be split to counters
and each customer can finish before the one that he was after in the queue.

Important thing to notice, this  function does clear space in the 
queue.

> > My second option is the pull, since again it implies that we are getting back
> > something from the queue and not just waiting for event.
> 
> I'll think a bit more about it.

Thanks, 

Best,
Ori
  
Ori Kam Feb. 17, 2022, 2:16 p.m. UTC | #14
Hi Andrew,

> -----Original Message-----
> From: Ori Kam
> Sent: Thursday, February 17, 2022 1:09 PM
> Subject: RE: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> 
> Hi Andrew,
> 
> > -----Original Message-----
> > From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> > Sent: Thursday, February 17, 2022 12:53 PM
> > Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> >
> > Hi Ori,
> >
> > On 2/16/22 17:53, Ori Kam wrote:
> > > Hi Andew,
> > >
> > >> -----Original Message-----
> > >> From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> > >> Sent: Wednesday, February 16, 2022 3:34 PM
> > >> Subject: Re: [PATCH v5 03/10] ethdev: bring in async queue-based flow rules operations
> > >>
> > >> On 2/12/22 05:19, Alexander Kozyrev wrote:
> > >>> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> > >>>> On 2/11/22 05:26, Alexander Kozyrev wrote:
> > >>>>> A new, faster, queue-based flow rules management mechanism is needed
> > >>>> for
> > >>>>> applications offloading rules inside the datapath. This asynchronous
> > >>>>> and lockless mechanism frees the CPU for further packet processing and
> > >>>>> reduces the performance impact of the flow rules creation/destruction
> > >>>>> on the datapath. Note that queues are not thread-safe and the queue
> > >>>>> should be accessed from the same thread for all queue operations.
> > >>>>> It is the responsibility of the app to sync the queue functions in case
> > >>>>> of multi-threaded access to the same queue.
> > >>>>>
> > >>>>> The rte_flow_q_flow_create() function enqueues a flow creation to the
> > >>>>> requested queue. It benefits from already configured resources and sets
> > >>>>> unique values on top of item and action templates. A flow rule is enqueued
> > >>>>> on the specified flow queue and offloaded asynchronously to the
> > >>>> hardware.
> > >>>>> The function returns immediately to spare CPU for further packet
> > >>>>> processing. The application must invoke the rte_flow_q_pull() function
> > >>>>> to complete the flow rule operation offloading, to clear the queue, and to
> > >>>>> receive the operation status. The rte_flow_q_flow_destroy() function
> > >>>>> enqueues a flow destruction to the requested queue.
> > >>>>>
> > >>>>> Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
> > >>>>> Acked-by: Ori Kam <orika@nvidia.com>
> > >>
> > >> [snip]
> > >>
> > >>>>> +
> > >>>>> +- Available operation types: rule creation, rule destruction,
> > >>>>> +  indirect rule creation, indirect rule destruction, indirect rule update.
> > >>>>> +
> > >>>>> +- Operations may be reordered within a queue.
> > >>>>
> > >>>> Do we want to have barriers?
> > >>>> E.g. create rule, destroy the same rule -> reoder -> destroy fails, rule
> > >>>> lives forever.
> > >>>
> > >>> API design is crafter with the throughput as the main goal in mind.
> > >>> We allow user to enforce any ordering outside these functions.
> > >>> Another point that not all PMDs/NIC will have this out-of-order execution.
> > >>
> > >> Throughput is nice, but there more important requirements
> > >> which must be satistied before talking about performance.
> > >> Could you explain me what I should do based on which
> > >> information from NIC in order to solve above problem?
> > >>
> > >
> > > The idea is that if application has dependency between the rules/ rules operations.
> > > It should wait for the completion of the operation before sending the dependent operation.
> > > In the example you provided above, according to the documeation application should wait
> > > for the completion of the flow creation before destroying it.
> >
> > I see, thanks. May be I read documentation not that attentive.
> > I'll reread on the next version review cycle.
> >
> > >>>>> +
> > >>>>> +- Operations can be postponed and pushed to NIC in batches.
> > >>>>> +
> > >>>>> +- Results pulling must be done on time to avoid queue overflows.
> > >>>>
> > >>>> polling? (as libc poll() which checks status of file descriptors)
> > >>>> it is not pulling the door to open it :)
> > >>>
> > >>> poll waits for some event on a file descriptor as it title says.
> > >>> And then user has to invoke read() to actually get any info from the fd.
> > >>> The point of our function is to return the result immediately, thus pulling.
> > >>> We had many names appearing in the thread for these functions.
> > >>> As we know, naming variables is the second hardest thing in programming.
> > >>> I wanted this pull for results pulling be a counterpart for the push for
> > >>> pushing the operations to a NIC. Another idea is pop/push pair, but they are
> > >>> more like for operations only, not for results.
> > >>> Having said that I'm at the point of accepting any name here.
> > >>
> > >> I agree that it is hard to choose good naming.
> > >> Just want to say that polling is not alway waiting.
> > >>
> > >> poll - check the status of (a device), especially as part of a repeated
> > >> cycle.
> > >>
> > >> Here we're checking status of flow engine requests and yes,
> > >> finally in a repeated cycle.
> > >>
> > >> [snip]
> > >>
> > >>>>> +/**
> > >>>>> + * @warning
> > >>>>> + * @b EXPERIMENTAL: this API may change without prior notice.
> > >>>>> + *
> > >>>>> + * Queue operation attributes.
> > >>>>> + */
> > >>>>> +struct rte_flow_q_ops_attr {
> > >>>>> +	/**
> > >>>>> +	 * The user data that will be returned on the completion events.
> > >>>>> +	 */
> > >>>>> +	void *user_data;
> > >>>>
> > >>>> IMHO it must not be hiddne in attrs. It is a key information
> > >>>> which is used to understand the opration result. It should
> > >>>> be passed separately.
> > >>>
> > >>> Maybe, on the other hand it is optional and may not be needed by an application.
> > >>
> > >> I don't understand how it is possible. Without it application
> > >> don't know fate of its requests.
> > >>
> > > IMHO since user_data should be in all related operations API
> > > along with the attr, splitting the user_data will just add extra parameter
> > > to each function call. Since we have number of functions and will add
> > > more in future I think it will be best to keep it in this location.
> >
> > My problem with hiding user_data inside attr is that
> > 'user_data' is not an auxiliary attribute defining extra
> > properties of the request. It is a key information.
> > May be attr is not an ideal name for such grouping
> > of parameters. Unfortunately I have no better ideas right now.
> >
> I understand your point, if you don't have objections lets keep the current one
> and if needed we will modify.
> Is that O.K?
> 

Thinking about it again,
lets move it to a dedecated parameter.

Ori
> > Andrew.
  
Thomas Monjalon Feb. 17, 2022, 2:34 p.m. UTC | #15
17/02/2022 15:16, Ori Kam:
> From: Ori Kam
> > From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> > > On 2/16/22 17:53, Ori Kam wrote:
> > > > From: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> > > >> On 2/12/22 05:19, Alexander Kozyrev wrote:
> > > >>> On Fri, Feb 11, 2022 7:42 Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>:
> > > >>>>> +/**
> > > >>>>> + * @warning
> > > >>>>> + * @b EXPERIMENTAL: this API may change without prior notice.
> > > >>>>> + *
> > > >>>>> + * Queue operation attributes.
> > > >>>>> + */
> > > >>>>> +struct rte_flow_q_ops_attr {
> > > >>>>> +	/**
> > > >>>>> +	 * The user data that will be returned on the completion events.
> > > >>>>> +	 */
> > > >>>>> +	void *user_data;
> > > >>>>
> > > >>>> IMHO it must not be hiddne in attrs. It is a key information
> > > >>>> which is used to understand the opration result. It should
> > > >>>> be passed separately.
> > > >>>
> > > >>> Maybe, on the other hand it is optional and may not be needed by an application.
> > > >>
> > > >> I don't understand how it is possible. Without it application
> > > >> don't know fate of its requests.
> > > >>
> > > > IMHO since user_data should be in all related operations API
> > > > along with the attr, splitting the user_data will just add extra parameter
> > > > to each function call. Since we have number of functions and will add
> > > > more in future I think it will be best to keep it in this location.
> > >
> > > My problem with hiding user_data inside attr is that
> > > 'user_data' is not an auxiliary attribute defining extra
> > > properties of the request. It is a key information.
> > > May be attr is not an ideal name for such grouping
> > > of parameters. Unfortunately I have no better ideas right now.
> > >
> > I understand your point, if you don't have objections lets keep the current one
> > and if needed we will modify.
> > Is that O.K?
> 
> Thinking about it again,
> lets move it to a dedecated parameter.

I'm OK with the decision of moving user_data as a function parameter.
  

Patch

diff --git a/doc/guides/prog_guide/img/rte_flow_q_init.svg b/doc/guides/prog_guide/img/rte_flow_q_init.svg
new file mode 100644
index 0000000000..96160bde42
--- /dev/null
+++ b/doc/guides/prog_guide/img/rte_flow_q_init.svg
@@ -0,0 +1,205 @@ 
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+
+<!-- Copyright(c) 2022 NVIDIA Corporation & Affiliates -->
+
+<svg
+   width="485"
+   height="535"
+   overflow="hidden"
+   version="1.1"
+   id="svg61"
+   sodipodi:docname="rte_flow_q_init.svg"
+   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview63"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="1.517757"
+     inkscape:cx="242.79249"
+     inkscape:cy="267.17057"
+     inkscape:window-width="2400"
+     inkscape:window-height="1271"
+     inkscape:window-x="2391"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g59" />
+  <defs
+     id="defs5">
+    <clipPath
+       id="clip0">
+      <rect
+         x="0"
+         y="0"
+         width="485"
+         height="535"
+         id="rect2" />
+    </clipPath>
+  </defs>
+  <g
+     clip-path="url(#clip0)"
+     id="g59">
+    <rect
+       x="0"
+       y="0"
+       width="485"
+       height="535"
+       fill="#FFFFFF"
+       id="rect7" />
+    <rect
+       x="0.500053"
+       y="79.5001"
+       width="482"
+       height="59"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#A6A6A6"
+       id="rect9" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="24"
+       transform="translate(121.6 116)"
+       id="text13">
+         rte_eth_dev_configure
+         <tspan
+   font-size="24"
+   x="224.007"
+   y="0"
+   id="tspan11">()</tspan></text>
+    <rect
+       x="0.500053"
+       y="158.5"
+       width="482"
+       height="59"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect15" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="24"
+       transform="translate(140.273 195)"
+       id="text17">
+         rte_flow_configure()
+      </text>
+    <rect
+       x="0.500053"
+       y="236.5"
+       width="482"
+       height="60"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect19" />
+    <text
+       font-family="Calibri, Calibri_MSFontService, sans-serif"
+       font-weight="400"
+       font-size="24px"
+       id="text21"
+       x="63.425903"
+       y="274">rte_flow_pattern_template_create()</text>
+    <rect
+       x="0.500053"
+       y="316.5"
+       width="482"
+       height="59"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect23" />
+    <text
+       font-family="Calibri, Calibri_MSFontService, sans-serif"
+       font-weight="400"
+       font-size="24px"
+       id="text27"
+       x="69.379204"
+       y="353">rte_flow_actions_template_create()</text>
+    <rect
+       x="0.500053"
+       y="0.500053"
+       width="482"
+       height="60"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#A6A6A6"
+       id="rect29" />
+    <text
+       font-family="Calibri, Calibri_MSFontService, sans-serif"
+       font-weight="400"
+       font-size="24px"
+       transform="translate(177.233,37)"
+       id="text33">rte_eal_init()</text>
+    <path
+       d="M2-1.09108e-05 2.00005 9.2445-1.99995 9.24452-2 1.09108e-05ZM6.00004 7.24448 0.000104987 19.2445-5.99996 7.24455Z"
+       transform="matrix(-1 0 0 1 241 60)"
+       id="path35" />
+    <path
+       d="M2-1.08133e-05 2.00005 9.41805-1.99995 9.41807-2 1.08133e-05ZM6.00004 7.41802 0.000104987 19.4181-5.99996 7.41809Z"
+       transform="matrix(-1 0 0 1 241 138)"
+       id="path37" />
+    <path
+       d="M2-1.09108e-05 2.00005 9.2445-1.99995 9.24452-2 1.09108e-05ZM6.00004 7.24448 0.000104987 19.2445-5.99996 7.24455Z"
+       transform="matrix(-1 0 0 1 241 217)"
+       id="path39" />
+    <rect
+       x="0.500053"
+       y="395.5"
+       width="482"
+       height="59"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect41" />
+    <text
+       font-family="Calibri, Calibri_MSFontService, sans-serif"
+       font-weight="400"
+       font-size="24px"
+       id="text47"
+       x="76.988998"
+       y="432">rte_flow_template_table_create()</text>
+    <path
+       d="M2-1.05859e-05 2.00005 9.83526-1.99995 9.83529-2 1.05859e-05ZM6.00004 7.83524 0.000104987 19.8353-5.99996 7.83531Z"
+       transform="matrix(-1 0 0 1 241 296)"
+       id="path49" />
+    <path
+       d="M243 375 243 384.191 239 384.191 239 375ZM247 382.191 241 394.191 235 382.191Z"
+       id="path51" />
+    <rect
+       x="0.500053"
+       y="473.5"
+       width="482"
+       height="60"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#A6A6A6"
+       id="rect53" />
+    <text
+       font-family="Calibri, Calibri_MSFontService, sans-serif"
+       font-weight="400"
+       font-size="24px"
+       id="text55"
+       x="149.30299"
+       y="511">rte_eth_dev_start()</text>
+    <path
+       d="M245 454 245 463.191 241 463.191 241 454ZM249 461.191 243 473.191 237 461.191Z"
+       id="path57" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/rte_flow_q_usage.svg b/doc/guides/prog_guide/img/rte_flow_q_usage.svg
new file mode 100644
index 0000000000..a1f6c0a0a8
--- /dev/null
+++ b/doc/guides/prog_guide/img/rte_flow_q_usage.svg
@@ -0,0 +1,351 @@ 
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+
+<!-- Copyright(c) 2022 NVIDIA Corporation & Affiliates -->
+
+<svg
+   width="880"
+   height="610"
+   overflow="hidden"
+   version="1.1"
+   id="svg103"
+   sodipodi:docname="rte_flow_q_usage.svg"
+   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview105"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="1.3311475"
+     inkscape:cx="439.84606"
+     inkscape:cy="305.37562"
+     inkscape:window-width="2400"
+     inkscape:window-height="1271"
+     inkscape:window-x="2391"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g101" />
+  <defs
+     id="defs5">
+    <clipPath
+       id="clip0">
+      <rect
+         x="0"
+         y="0"
+         width="880"
+         height="610"
+         id="rect2" />
+    </clipPath>
+  </defs>
+  <g
+     clip-path="url(#clip0)"
+     id="g101">
+    <rect
+       x="0"
+       y="0"
+       width="880"
+       height="610"
+       fill="#FFFFFF"
+       id="rect7" />
+    <rect
+       x="333.5"
+       y="0.500053"
+       width="234"
+       height="45"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#A6A6A6"
+       id="rect9" />
+    <text
+       font-family="Consolas, Consolas_MSFontService, sans-serif"
+       font-weight="400"
+       font-size="19px"
+       transform="translate(357.196,29)"
+       id="text11">rte_eth_rx_burst()</text>
+    <rect
+       x="333.5"
+       y="63.5001"
+       width="234"
+       height="45"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       id="rect13" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(394.666 91)"
+       id="text17">analyze <tspan
+   font-size="19"
+   x="60.9267"
+   y="0"
+   id="tspan15">packet </tspan></text>
+    <rect
+       x="572.5"
+       y="279.5"
+       width="234"
+       height="46"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect19" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(591.429 308)"
+       id="text21">rte_flow_q_flow_create()</text>
+    <path
+       d="M333.5 384 450.5 350.5 567.5 384 450.5 417.5Z"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       fill-rule="evenodd"
+       id="path23" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(430.069 378)"
+       id="text27">more <tspan
+   font-size="19"
+   x="-12.94"
+   y="23"
+   id="tspan25">packets?</tspan></text>
+    <path
+       d="M689.249 325.5 689.249 338.402 450.5 338.402 450.833 338.069 450.833 343.971 450.167 343.971 450.167 337.735 688.916 337.735 688.582 338.069 688.582 325.5ZM454.5 342.638 450.5 350.638 446.5 342.638Z"
+       id="path29" />
+    <path
+       d="M450.833 45.5 450.833 56.8197 450.167 56.8197 450.167 45.5001ZM454.5 55.4864 450.5 63.4864 446.5 55.4864Z"
+       id="path31" />
+    <path
+       d="M450.833 108.5 450.833 120.375 450.167 120.375 450.167 108.5ZM454.5 119.041 450.5 127.041 446.5 119.041Z"
+       id="path33" />
+    <path
+       d="M451.833 507.5 451.833 533.61 451.167 533.61 451.167 507.5ZM455.5 532.277 451.5 540.277 447.5 532.277Z"
+       id="path35" />
+    <path
+       d="M0 0.333333-23.9993 0.333333-23.666 0-23.666 141.649-23.9993 141.316 562.966 141.316 562.633 141.649 562.633 124.315 563.299 124.315 563.299 141.983-24.3327 141.983-24.3327-0.333333 0-0.333333ZM558.966 125.649 562.966 117.649 566.966 125.649Z"
+       transform="matrix(-6.12323e-17 -1 -1 6.12323e-17 451.149 585.466)"
+       id="path37" />
+    <path
+       d="M333.5 160.5 450.5 126.5 567.5 160.5 450.5 194.5Z"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       fill-rule="evenodd"
+       id="path39" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(417.576 155)"
+       id="text43">add new <tspan
+   font-size="19"
+   x="13.2867"
+   y="23"
+   id="tspan41">rule?</tspan></text>
+    <path
+       d="M567.5 160.167 689.267 160.167 689.267 273.228 688.6 273.228 688.6 160.5 688.933 160.833 567.5 160.833ZM692.933 271.894 688.933 279.894 684.933 271.894Z"
+       id="path45" />
+    <rect
+       x="602.5"
+       y="127.5"
+       width="46"
+       height="30"
+       stroke="#000000"
+       stroke-width="0.666667"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       id="rect47" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(611.34 148)"
+       id="text49">yes</text>
+    <rect
+       x="254.5"
+       y="126.5"
+       width="46"
+       height="31"
+       stroke="#000000"
+       stroke-width="0.666667"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       id="rect51" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(267.182 147)"
+       id="text53">no</text>
+    <path
+       d="M0-0.333333 251.563-0.333333 251.563 298.328 8.00002 298.328 8.00002 297.662 251.229 297.662 250.896 297.995 250.896 0 251.229 0.333333 0 0.333333ZM9.33333 301.995 1.33333 297.995 9.33333 293.995Z"
+       transform="matrix(1 0 0 -1 567.5 383.495)"
+       id="path55" />
+    <path
+       d="M86.5001 213.5 203.5 180.5 320.5 213.5 203.5 246.5Z"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       fill-rule="evenodd"
+       id="path57" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(159.155 208)"
+       id="text61">destroy the <tspan
+   font-size="19"
+   x="24.0333"
+   y="23"
+   id="tspan59">rule?</tspan></text>
+    <path
+       d="M0-0.333333 131.029-0.333333 131.029 12.9778 130.363 12.9778 130.363 0 130.696 0.333333 0 0.333333ZM134.696 11.6445 130.696 19.6445 126.696 11.6445Z"
+       transform="matrix(-1 1.22465e-16 1.22465e-16 1 334.196 160.5)"
+       id="path63" />
+    <rect
+       x="81.5001"
+       y="280.5"
+       width="234"
+       height="45"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect65" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(96.2282 308)"
+       id="text67">rte_flow_q_flow_destroy()</text>
+    <path
+       d="M0 0.333333-24.0001 0.333333-23.6667 0-23.6667 49.9498-24.0001 49.6165 121.748 49.6165 121.748 59.958 121.082 59.958 121.082 49.9498 121.415 50.2832-24.3334 50.2832-24.3334-0.333333 0-0.333333ZM125.415 58.6247 121.415 66.6247 117.415 58.6247Z"
+       transform="matrix(-1 0 0 1 319.915 213.5)"
+       id="path69" />
+    <path
+       d="M86.5001 213.833 62.5002 213.833 62.8335 213.5 62.8335 383.95 62.5002 383.617 327.511 383.617 327.511 384.283 62.1668 384.283 62.1668 213.167 86.5001 213.167ZM326.178 379.95 334.178 383.95 326.178 387.95Z"
+       id="path71" />
+    <path
+       d="M0-0.333333 12.8273-0.333333 12.8273 252.111 12.494 251.778 18.321 251.778 18.321 252.445 12.1607 252.445 12.1607 0 12.494 0.333333 0 0.333333ZM16.9877 248.111 24.9877 252.111 16.9877 256.111Z"
+       transform="matrix(1.83697e-16 1 1 -1.83697e-16 198.5 325.5)"
+       id="path73" />
+    <rect
+       x="334.5"
+       y="540.5"
+       width="234"
+       height="45"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect75" />
+    <text
+       font-family="Calibri, Calibri_MSFontService, sans-serif"
+       font-weight="400"
+       font-size="19px"
+       id="text77"
+       x="385.08301"
+       y="569">rte_flow_q_pull()</text>
+    <rect
+       x="334.5"
+       y="462.5"
+       width="234"
+       height="45"
+       stroke="#000000"
+       stroke-width="1.33333"
+       stroke-miterlimit="8"
+       fill="#FFFFFF"
+       id="rect79" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(379.19 491)"
+       id="text81">rte_flow_q_push()</text>
+    <path
+       d="M450.833 417.495 451.402 455.999 450.735 456.008 450.167 417.505ZM455.048 454.611 451.167 462.669 447.049 454.729Z"
+       id="path83" />
+    <rect
+       x="0.500053"
+       y="287.5"
+       width="46"
+       height="30"
+       stroke="#000000"
+       stroke-width="0.666667"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       id="rect85" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(12.8617 308)"
+       id="text87">no</text>
+    <rect
+       x="357.5"
+       y="223.5"
+       width="47"
+       height="31"
+       stroke="#000000"
+       stroke-width="0.666667"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       id="rect89" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(367.001 244)"
+       id="text91">yes</text>
+    <rect
+       x="469.5"
+       y="421.5"
+       width="46"
+       height="30"
+       stroke="#000000"
+       stroke-width="0.666667"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       id="rect93" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(481.872 442)"
+       id="text95">no</text>
+    <rect
+       x="832.5"
+       y="223.5"
+       width="46"
+       height="31"
+       stroke="#000000"
+       stroke-width="0.666667"
+       stroke-miterlimit="8"
+       fill="#D9D9D9"
+       id="rect97" />
+    <text
+       font-family="Calibri,Calibri_MSFontService,sans-serif"
+       font-weight="400"
+       font-size="19"
+       transform="translate(841.777 244)"
+       id="text99">yes</text>
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 5391648833..5d47f3bd21 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -3607,12 +3607,16 @@  Expected number of counters or meters in an application, for example,
 allow PMD to prepare and optimize NIC memory layout in advance.
 ``rte_flow_configure()`` must be called before any flow rule is created,
 but after an Ethernet device is configured.
+It also creates flow queues for asynchronous flow rules operations via
+queue-based API, see `Asynchronous operations`_ section.
 
 .. code-block:: c
 
    int
    rte_flow_configure(uint16_t port_id,
                      const struct rte_flow_port_attr *port_attr,
+                     uint16_t nb_queue,
+                     const struct rte_flow_queue_attr *queue_attr[],
                      struct rte_flow_error *error);
 
 Information about resources that can benefit from pre-allocation can be
@@ -3737,7 +3741,7 @@  and pattern and actions templates are created.
 
 .. code-block:: c
 
-	rte_flow_configure(port, *port_attr, *error);
+	rte_flow_configure(port, *port_attr, nb_queue, *queue_attr, *error);
 
 	struct rte_flow_pattern_template *pattern_templates[0] =
 		rte_flow_pattern_template_create(port, &itr, &pattern, &error);
@@ -3750,6 +3754,167 @@  and pattern and actions templates are created.
 				*actions_templates, nb_actions_templates,
 				*error);
 
+Asynchronous operations
+-----------------------
+
+Flow rules management can be done via special lockless flow management queues.
+- Queue operations are asynchronous and not thread-safe.
+
+- Operations can thus be invoked by the app's datapath,
+  packet processing can continue while queue operations are processed by NIC.
+
+- The queue number is configured at initialization stage.
+
+- Available operation types: rule creation, rule destruction,
+  indirect rule creation, indirect rule destruction, indirect rule update.
+
+- Operations may be reordered within a queue.
+
+- Operations can be postponed and pushed to NIC in batches.
+
+- Results pulling must be done on time to avoid queue overflows.
+
+- User data is returned as part of the result to identify an operation.
+
+- Flow handle is valid once the creation operation is enqueued and must be
+  destroyed even if the operation is not successful and the rule is not inserted.
+
+The asynchronous flow rule insertion logic can be broken into two phases.
+
+1. Initialization stage as shown here:
+
+.. _figure_rte_flow_q_init:
+
+.. figure:: img/rte_flow_q_init.*
+
+2. Main loop as presented on a datapath application example:
+
+.. _figure_rte_flow_q_usage:
+
+.. figure:: img/rte_flow_q_usage.*
+
+Enqueue creation operation
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Enqueueing a flow rule creation operation is similar to simple creation.
+
+.. code-block:: c
+
+	struct rte_flow *
+	rte_flow_q_flow_create(uint16_t port_id,
+				uint32_t queue_id,
+				const struct rte_flow_q_ops_attr *q_ops_attr,
+				struct rte_flow_template_table *template_table,
+				const struct rte_flow_item pattern[],
+				uint8_t pattern_template_index,
+				const struct rte_flow_action actions[],
+				uint8_t actions_template_index,
+				struct rte_flow_error *error);
+
+A valid handle in case of success is returned. It must be destroyed later
+by calling ``rte_flow_q_flow_destroy()`` even if the rule is rejected by HW.
+
+Enqueue destruction operation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Enqueueing a flow rule destruction operation is similar to simple destruction.
+
+.. code-block:: c
+
+	int
+	rte_flow_q_flow_destroy(uint16_t port_id,
+				uint32_t queue_id,
+				const struct rte_flow_q_ops_attr *q_ops_attr,
+				struct rte_flow *flow,
+				struct rte_flow_error *error);
+
+Push enqueued operations
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Pushing all internally stored rules from a queue to the NIC.
+
+.. code-block:: c
+
+	int
+	rte_flow_q_push(uint16_t port_id,
+			uint32_t queue_id,
+			struct rte_flow_error *error);
+
+There is the postpone attribute in the queue operation attributes.
+When it is set, multiple operations can be bulked together and not sent to HW
+right away to save SW/HW interactions and prioritize throughput over latency.
+The application must invoke this function to actually push all outstanding
+operations to HW in this case.
+
+Pull enqueued operations
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Pulling asynchronous operations results.
+
+The application must invoke this function in order to complete asynchronous
+flow rule operations and to receive flow rule operations statuses.
+
+.. code-block:: c
+
+	int
+	rte_flow_q_pull(uint16_t port_id,
+			uint32_t queue_id,
+			struct rte_flow_q_op_res res[],
+			uint16_t n_res,
+			struct rte_flow_error *error);
+
+Multiple outstanding operation results can be pulled simultaneously.
+User data may be provided during a flow creation/destruction in order
+to distinguish between multiple operations. User data is returned as part
+of the result to provide a method to detect which operation is completed.
+
+Enqueue indirect action creation operation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Asynchronous version of indirect action creation API.
+
+.. code-block:: c
+
+	struct rte_flow_action_handle *
+	rte_flow_q_action_handle_create(uint16_t port_id,
+			uint32_t queue_id,
+			const struct rte_flow_q_ops_attr *q_ops_attr,
+			const struct rte_flow_indir_action_conf *indir_action_conf,
+			const struct rte_flow_action *action,
+			struct rte_flow_error *error);
+
+A valid handle in case of success is returned. It must be destroyed later by
+calling ``rte_flow_q_action_handle_destroy()`` even if the rule is rejected.
+
+Enqueue indirect action destruction operation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Asynchronous version of indirect action destruction API.
+
+.. code-block:: c
+
+	int
+	rte_flow_q_action_handle_destroy(uint16_t port_id,
+			uint32_t queue_id,
+			const struct rte_flow_q_ops_attr *q_ops_attr,
+			struct rte_flow_action_handle *action_handle,
+			struct rte_flow_error *error);
+
+Enqueue indirect action update operation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Asynchronous version of indirect action update API.
+
+.. code-block:: c
+
+	int
+	rte_flow_q_action_handle_update(uint16_t port_id,
+			uint32_t queue_id,
+			const struct rte_flow_q_ops_attr *q_ops_attr,
+			struct rte_flow_action_handle *action_handle,
+			const void *update,
+			struct rte_flow_error *error);
+
 .. _flow_isolated_mode:
 
 Flow isolated mode
diff --git a/doc/guides/rel_notes/release_22_03.rst b/doc/guides/rel_notes/release_22_03.rst
index 6656b35295..87cea8a966 100644
--- a/doc/guides/rel_notes/release_22_03.rst
+++ b/doc/guides/rel_notes/release_22_03.rst
@@ -83,6 +83,14 @@  New Features
     ``rte_flow_template_table_destroy``, ``rte_flow_pattern_template_destroy``
     and ``rte_flow_actions_template_destroy``.
 
+  * ethdev: Added ``rte_flow_q_flow_create`` and ``rte_flow_q_flow_destroy``
+    API to enqueue flow creaion/destruction operations asynchronously as well
+    as ``rte_flow_q_pull`` to poll and retrieve results of these operations
+    and ``rte_flow_q_push`` to push all the in-flight operations to the NIC.
+    Introduced asynchronous API for indirect actions management as well:
+    ``rte_flow_q_action_handle_create``, ``rte_flow_q_action_handle_destroy``
+    and ``rte_flow_q_action_handle_update``.
+
 * **Updated AF_XDP PMD**
 
   * Added support for libxdp >=v1.2.2.
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index b53f8c9b89..aca5bac2da 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1415,6 +1415,8 @@  rte_flow_info_get(uint16_t port_id,
 int
 rte_flow_configure(uint16_t port_id,
 		   const struct rte_flow_port_attr *port_attr,
+		   uint16_t nb_queue,
+		   const struct rte_flow_queue_attr *queue_attr[],
 		   struct rte_flow_error *error)
 {
 	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
@@ -1424,7 +1426,8 @@  rte_flow_configure(uint16_t port_id,
 		return -rte_errno;
 	if (likely(!!ops->configure)) {
 		return flow_err(port_id,
-				ops->configure(dev, port_attr, error),
+				ops->configure(dev, port_attr,
+					       nb_queue, queue_attr, error),
 				error);
 	}
 	return rte_flow_error_set(error, ENOTSUP,
@@ -1578,3 +1581,173 @@  rte_flow_template_table_destroy(uint16_t port_id,
 				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
 				  NULL, rte_strerror(ENOTSUP));
 }
+
+struct rte_flow *
+rte_flow_q_flow_create(uint16_t port_id,
+		       uint32_t queue_id,
+		       const struct rte_flow_q_ops_attr *q_ops_attr,
+		       struct rte_flow_template_table *template_table,
+		       const struct rte_flow_item pattern[],
+		       uint8_t pattern_template_index,
+		       const struct rte_flow_action actions[],
+		       uint8_t actions_template_index,
+		       struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+	struct rte_flow *flow;
+
+	if (unlikely(!ops))
+		return NULL;
+	if (likely(!!ops->q_flow_create)) {
+		flow = ops->q_flow_create(dev, queue_id,
+					  q_ops_attr, template_table,
+					  pattern, pattern_template_index,
+					  actions, actions_template_index,
+					  error);
+		if (flow == NULL)
+			flow_err(port_id, -rte_errno, error);
+		return flow;
+	}
+	rte_flow_error_set(error, ENOTSUP,
+			   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			   NULL, rte_strerror(ENOTSUP));
+	return NULL;
+}
+
+int
+rte_flow_q_flow_destroy(uint16_t port_id,
+			uint32_t queue_id,
+			const struct rte_flow_q_ops_attr *q_ops_attr,
+			struct rte_flow *flow,
+			struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->q_flow_destroy)) {
+		return flow_err(port_id,
+				ops->q_flow_destroy(dev, queue_id,
+						    q_ops_attr, flow, error),
+				error);
+	}
+	return rte_flow_error_set(error, ENOTSUP,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOTSUP));
+}
+
+struct rte_flow_action_handle *
+rte_flow_q_action_handle_create(uint16_t port_id,
+		uint32_t queue_id,
+		const struct rte_flow_q_ops_attr *q_ops_attr,
+		const struct rte_flow_indir_action_conf *indir_action_conf,
+		const struct rte_flow_action *action,
+		struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+	struct rte_flow_action_handle *handle;
+
+	if (unlikely(!ops))
+		return NULL;
+	if (unlikely(!ops->q_action_handle_create)) {
+		rte_flow_error_set(error, ENOSYS,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   rte_strerror(ENOSYS));
+		return NULL;
+	}
+	handle = ops->q_action_handle_create(dev, queue_id, q_ops_attr,
+					     indir_action_conf, action, error);
+	if (handle == NULL)
+		flow_err(port_id, -rte_errno, error);
+	return handle;
+}
+
+int
+rte_flow_q_action_handle_destroy(uint16_t port_id,
+		uint32_t queue_id,
+		const struct rte_flow_q_ops_attr *q_ops_attr,
+		struct rte_flow_action_handle *action_handle,
+		struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+	int ret;
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (unlikely(!ops->q_action_handle_destroy))
+		return rte_flow_error_set(error, ENOSYS,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					  NULL, rte_strerror(ENOSYS));
+	ret = ops->q_action_handle_destroy(dev, queue_id, q_ops_attr,
+					   action_handle, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_q_action_handle_update(uint16_t port_id,
+		uint32_t queue_id,
+		const struct rte_flow_q_ops_attr *q_ops_attr,
+		struct rte_flow_action_handle *action_handle,
+		const void *update,
+		struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+	int ret;
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (unlikely(!ops->q_action_handle_update))
+		return rte_flow_error_set(error, ENOSYS,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					  NULL, rte_strerror(ENOSYS));
+	ret = ops->q_action_handle_update(dev, queue_id, q_ops_attr,
+					  action_handle, update, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_q_push(uint16_t port_id,
+		uint32_t queue_id,
+		struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->q_push)) {
+		return flow_err(port_id,
+				ops->q_push(dev, queue_id, error),
+				error);
+	}
+	return rte_flow_error_set(error, ENOTSUP,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOTSUP));
+}
+
+int
+rte_flow_q_pull(uint16_t port_id,
+		uint32_t queue_id,
+		struct rte_flow_q_op_res res[],
+		uint16_t n_res,
+		struct rte_flow_error *error)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
+	int ret;
+
+	if (unlikely(!ops))
+		return -rte_errno;
+	if (likely(!!ops->q_pull)) {
+		ret = ops->q_pull(dev, queue_id, res, n_res, error);
+		return ret ? ret : flow_err(port_id, ret, error);
+	}
+	return rte_flow_error_set(error, ENOTSUP,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, rte_strerror(ENOTSUP));
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index e87db5a540..b0d4f33bfd 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -4862,6 +4862,10 @@  rte_flow_flex_item_release(uint16_t port_id,
  *
  */
 struct rte_flow_port_info {
+	/**
+	 * Number of queues for asynchronous operations.
+	 */
+	uint32_t nb_queues;
 	/**
 	 * Number of pre-configurable counter actions.
 	 * @see RTE_FLOW_ACTION_TYPE_COUNT
@@ -4879,6 +4883,17 @@  struct rte_flow_port_info {
 	uint32_t nb_meters;
 };
 
+/**
+ * Flow engine queue configuration.
+ */
+__extension__
+struct rte_flow_queue_attr {
+	/**
+	 * Number of flow rule operations a queue can hold.
+	 */
+	uint32_t size;
+};
+
 /**
  * @warning
  * @b EXPERIMENTAL: this API may change without prior notice.
@@ -4948,6 +4963,11 @@  struct rte_flow_port_attr {
  *   Port identifier of Ethernet device.
  * @param[in] port_attr
  *   Port configuration attributes.
+ * @param[in] nb_queue
+ *   Number of flow queues to be configured.
+ * @param[in] queue_attr
+ *   Array that holds attributes for each flow queue.
+ *   Number of elements is set in @p port_attr.nb_queues.
  * @param[out] error
  *   Perform verbose error reporting if not NULL.
  *   PMDs initialize this structure in case of error only.
@@ -4959,6 +4979,8 @@  __rte_experimental
 int
 rte_flow_configure(uint16_t port_id,
 		   const struct rte_flow_port_attr *port_attr,
+		   uint16_t nb_queue,
+		   const struct rte_flow_queue_attr *queue_attr[],
 		   struct rte_flow_error *error);
 
 /**
@@ -5221,6 +5243,318 @@  rte_flow_template_table_destroy(uint16_t port_id,
 		struct rte_flow_template_table *template_table,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Queue operation attributes.
+ */
+struct rte_flow_q_ops_attr {
+	/**
+	 * The user data that will be returned on the completion events.
+	 */
+	void *user_data;
+	 /**
+	  * When set, the requested action will not be sent to the HW immediately.
+	  * The application must call the rte_flow_queue_push to actually send it.
+	  */
+	uint32_t postpone:1;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue rule creation operation.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue used to insert the rule.
+ * @param[in] q_ops_attr
+ *   Rule creation operation attributes.
+ * @param[in] template_table
+ *   Template table to select templates from.
+ * @param[in] pattern
+ *   List of pattern items to be used.
+ *   The list order should match the order in the pattern template.
+ *   The spec is the only relevant member of the item that is being used.
+ * @param[in] pattern_template_index
+ *   Pattern template index in the table.
+ * @param[in] actions
+ *   List of actions to be used.
+ *   The list order should match the order in the actions template.
+ * @param[in] actions_template_index
+ *   Actions template index in the table.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   Handle on success, NULL otherwise and rte_errno is set.
+ *   The rule handle doesn't mean that the rule was offloaded.
+ *   Only completion result indicates that the rule was offloaded.
+ */
+__rte_experimental
+struct rte_flow *
+rte_flow_q_flow_create(uint16_t port_id,
+		       uint32_t queue_id,
+		       const struct rte_flow_q_ops_attr *q_ops_attr,
+		       struct rte_flow_template_table *template_table,
+		       const struct rte_flow_item pattern[],
+		       uint8_t pattern_template_index,
+		       const struct rte_flow_action actions[],
+		       uint8_t actions_template_index,
+		       struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue rule destruction operation.
+ *
+ * This function enqueues a destruction operation on the queue.
+ * Application should assume that after calling this function
+ * the rule handle is not valid anymore.
+ * Completion indicates the full removal of the rule from the HW.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to destroy the rule.
+ *   This must match the queue on which the rule was created.
+ * @param[in] q_ops_attr
+ *   Rule destroy operation attributes.
+ * @param[in] flow
+ *   Flow handle to be destroyed.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+__rte_experimental
+int
+rte_flow_q_flow_destroy(uint16_t port_id,
+			uint32_t queue_id,
+			const struct rte_flow_q_ops_attr *q_ops_attr,
+			struct rte_flow *flow,
+			struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue indirect action creation operation.
+ * @see rte_flow_action_handle_create
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue which is used to create the rule.
+ * @param[in] q_ops_attr
+ *   Queue operation attributes.
+ * @param[in] indir_action_conf
+ *   Action configuration for the indirect action object creation.
+ * @param[in] action
+ *   Specific configuration of the indirect action object.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   - (0) if success.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-ENOSYS) if underlying device does not support this functionality.
+ *   - (-EIO) if underlying device is removed.
+ *   - (-ENOENT) if action pointed by *action* handle was not found.
+ *   - (-EBUSY) if action pointed by *action* handle still used by some rules
+ *   rte_errno is also set.
+ */
+__rte_experimental
+struct rte_flow_action_handle *
+rte_flow_q_action_handle_create(uint16_t port_id,
+		uint32_t queue_id,
+		const struct rte_flow_q_ops_attr *q_ops_attr,
+		const struct rte_flow_indir_action_conf *indir_action_conf,
+		const struct rte_flow_action *action,
+		struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue indirect action destruction operation.
+ * The destroy queue must be the same
+ * as the queue on which the action was created.
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue which is used to destroy the rule.
+ * @param[in] q_ops_attr
+ *   Queue operation attributes.
+ * @param[in] action_handle
+ *   Handle for the indirect action object to be destroyed.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   - (0) if success.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-ENOSYS) if underlying device does not support this functionality.
+ *   - (-EIO) if underlying device is removed.
+ *   - (-ENOENT) if action pointed by *action* handle was not found.
+ *   - (-EBUSY) if action pointed by *action* handle still used by some rules
+ *   rte_errno is also set.
+ */
+__rte_experimental
+int
+rte_flow_q_action_handle_destroy(uint16_t port_id,
+		uint32_t queue_id,
+		const struct rte_flow_q_ops_attr *q_ops_attr,
+		struct rte_flow_action_handle *action_handle,
+		struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue indirect action update operation.
+ * @see rte_flow_action_handle_create
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue which is used to update the rule.
+ * @param[in] q_ops_attr
+ *   Queue operation attributes.
+ * @param[in] action_handle
+ *   Handle for the indirect action object to be updated.
+ * @param[in] update
+ *   Update profile specification used to modify the action pointed by handle.
+ *   *update* could be with the same type of the immediate action corresponding
+ *   to the *handle* argument when creating, or a wrapper structure includes
+ *   action configuration to be updated and bit fields to indicate the member
+ *   of fields inside the action to update.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   - (0) if success.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-ENOSYS) if underlying device does not support this functionality.
+ *   - (-EIO) if underlying device is removed.
+ *   - (-ENOENT) if action pointed by *action* handle was not found.
+ *   - (-EBUSY) if action pointed by *action* handle still used by some rules
+ *   rte_errno is also set.
+ */
+__rte_experimental
+int
+rte_flow_q_action_handle_update(uint16_t port_id,
+		uint32_t queue_id,
+		const struct rte_flow_q_ops_attr *q_ops_attr,
+		struct rte_flow_action_handle *action_handle,
+		const void *update,
+		struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Push all internally stored rules to the HW.
+ * Postponed rules are rules that were inserted with the postpone flag set.
+ * Can be used to notify the HW about batch of rules prepared by the SW to
+ * reduce the number of communications between the HW and SW.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue to be pushed.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *    0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+__rte_experimental
+int
+rte_flow_q_push(uint16_t port_id,
+		uint32_t queue_id,
+		struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Queue operation status.
+ */
+enum rte_flow_q_op_status {
+	/**
+	 * The operation was completed successfully.
+	 */
+	RTE_FLOW_Q_OP_SUCCESS,
+	/**
+	 * The operation was not completed successfully.
+	 */
+	RTE_FLOW_Q_OP_ERROR,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Queue operation results.
+ */
+__extension__
+struct rte_flow_q_op_res {
+	/**
+	 * Returns the status of the operation that this completion signals.
+	 */
+	enum rte_flow_q_op_status status;
+	/**
+	 * The user data that will be returned on the completion events.
+	 */
+	void *user_data;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Pull a rte flow operation.
+ * The application must invoke this function in order to complete
+ * the flow rule offloading and to retrieve the flow rule operation status.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to pull the operation.
+ * @param[out] res
+ *   Array of results that will be set.
+ * @param[in] n_res
+ *   Maximum number of results that can be returned.
+ *   This value is equal to the size of the res array.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *   PMDs initialize this structure in case of error only.
+ *
+ * @return
+ *   Number of results that were pulled,
+ *   a negative errno value otherwise and rte_errno is set.
+ */
+__rte_experimental
+int
+rte_flow_q_pull(uint16_t port_id,
+		uint32_t queue_id,
+		struct rte_flow_q_op_res res[],
+		uint16_t n_res,
+		struct rte_flow_error *error);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/ethdev/rte_flow_driver.h b/lib/ethdev/rte_flow_driver.h
index 2d96db1dc7..33dc57a15e 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -161,6 +161,8 @@  struct rte_flow_ops {
 	int (*configure)
 		(struct rte_eth_dev *dev,
 		 const struct rte_flow_port_attr *port_attr,
+		 uint16_t nb_queue,
+		 const struct rte_flow_queue_attr *queue_attr[],
 		 struct rte_flow_error *err);
 	/** See rte_flow_pattern_template_create() */
 	struct rte_flow_pattern_template *(*pattern_template_create)
@@ -199,6 +201,59 @@  struct rte_flow_ops {
 		(struct rte_eth_dev *dev,
 		 struct rte_flow_template_table *template_table,
 		 struct rte_flow_error *err);
+	/** See rte_flow_q_flow_create() */
+	struct rte_flow *(*q_flow_create)
+		(struct rte_eth_dev *dev,
+		 uint32_t queue_id,
+		 const struct rte_flow_q_ops_attr *q_ops_attr,
+		 struct rte_flow_template_table *template_table,
+		 const struct rte_flow_item pattern[],
+		 uint8_t pattern_template_index,
+		 const struct rte_flow_action actions[],
+		 uint8_t actions_template_index,
+		 struct rte_flow_error *err);
+	/** See rte_flow_q_flow_destroy() */
+	int (*q_flow_destroy)
+		(struct rte_eth_dev *dev,
+		 uint32_t queue_id,
+		 const struct rte_flow_q_ops_attr *q_ops_attr,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *err);
+	/** See rte_flow_q_action_handle_create() */
+	struct rte_flow_action_handle *(*q_action_handle_create)
+		(struct rte_eth_dev *dev,
+		 uint32_t queue_id,
+		 const struct rte_flow_q_ops_attr *q_ops_attr,
+		 const struct rte_flow_indir_action_conf *indir_action_conf,
+		 const struct rte_flow_action *action,
+		 struct rte_flow_error *err);
+	/** See rte_flow_q_action_handle_destroy() */
+	int (*q_action_handle_destroy)
+		(struct rte_eth_dev *dev,
+		 uint32_t queue_id,
+		 const struct rte_flow_q_ops_attr *q_ops_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 struct rte_flow_error *error);
+	/** See rte_flow_q_action_handle_update() */
+	int (*q_action_handle_update)
+		(struct rte_eth_dev *dev,
+		 uint32_t queue_id,
+		 const struct rte_flow_q_ops_attr *q_ops_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update,
+		 struct rte_flow_error *error);
+	/** See rte_flow_q_push() */
+	int (*q_push)
+		(struct rte_eth_dev *dev,
+		 uint32_t queue_id,
+		 struct rte_flow_error *err);
+	/** See rte_flow_q_pull() */
+	int (*q_pull)
+		(struct rte_eth_dev *dev,
+		 uint32_t queue_id,
+		 struct rte_flow_q_op_res res[],
+		 uint16_t n_res,
+		 struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 5fd2108895..46a4151053 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -268,6 +268,13 @@  EXPERIMENTAL {
 	rte_flow_actions_template_destroy;
 	rte_flow_template_table_create;
 	rte_flow_template_table_destroy;
+	rte_flow_q_flow_create;
+	rte_flow_q_flow_destroy;
+	rte_flow_q_action_handle_create;
+	rte_flow_q_action_handle_destroy;
+	rte_flow_q_action_handle_update;
+	rte_flow_q_push;
+	rte_flow_q_pull;
 };
 
 INTERNAL {