[v5] ethdev: add template table resize API

Message ID 20240211093053.397469-1-getelson@nvidia.com (mailing list archive)
State Changes Requested, archived
Delegated to: Ferruh Yigit
Headers
Series [v5] ethdev: add template table resize API |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/iol-testing warning apply patch failure
ci/Intel-compilation success Compilation OK
ci/github-robot: build success github build: passed
ci/intel-Testing success Testing PASS
ci/intel-Functional success Functional PASS

Commit Message

Gregory Etelson Feb. 11, 2024, 9:30 a.m. UTC
  Template table creation API sets table flows capacity.
If application needs more flows then the table was designed for,
the following procedures must be completed:
1. Create a new template table with larger flows capacity.
2. Re-create existing flows in the new table and delete flows from
   the original table.
3. Destroy original table.

Application cannot always execute that procedure:
* Port may not have sufficient resources to allocate a new table
  while maintaining original table.
* Application may not have existing flows "recipes" to re-create
  flows in a new table.

The patch defines a new API that allows application to resize
existing template table:

* Resizable template table must be created with the
RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE_TABLE bit set.

* Application resizes existing table with the
  `rte_flow_template_table_resize()` function call.
  The table resize procedure updates the table maximal flow number
  only. Other table attributes are not affected by the table resize.
  ** The table resize procedure must not interrupt
     existing table flows operations in hardware.
  ** The table resize procedure must not alter flow handlers held by
     application.

* After `rte_flow_template_table_resize()` returned, application must
  update all existing table flow rules by calling
  `rte_flow_async_update_resized()`.
  The table resize procedure does not change application flow handler.
  However, flow object can reference internal PMD resources that are
  obsolete after table resize.
  `rte_flow_async_update_resized()` moves internal flow references
  to the updated table resources.
  The flow update must not interrupt hardware flow operations.

* When all table flow were updated, application must call
  `rte_flow_template_table_resize_complete()`.
  The function releases PMD resources related to the original
  table.
  Application can start new table resize after
  `rte_flow_template_table_resize_complete()` returned.

Testpmd commands:

* Create resizable template table
flow template_table <port-id> create table_id <tbl-id> resizable \
  [transfer|ingress|egres] group <group-id> \
  rules_number <initial table capacity> \
  pattern_template  <pt1> [ pattern_template <pt2> [ ... ]] \
  actions_template  <at1> [ actions_template <at2> [ ... ]]

* Resize table:
flow template_table <tbl-id> resize table_resize_id <tbl-id> \
  table_resize_rules_num <new table capacity>

* Queue a flow update:
flow queue <port-id> update_resized <tbl-id> rule <flow-id>

* Complete table resize:
flow template_table <port-id> resize_complete table <tbl-id>

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Update the patch comment.
    Add table resize commands to testpmd user guide.
v3: Rename RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE macro.
v4: Remove inline.
    Add use case to rte_flow.rst.
v5: update API guide in the rte_flow.rst.	
---
 app/test-pmd/cmdline_flow.c                 |  86 ++++++++++++++-
 app/test-pmd/config.c                       | 102 +++++++++++++++++
 app/test-pmd/testpmd.h                      |   6 +
 doc/guides/howto/rte_flow.rst               | 116 ++++++++++++++++++++
 doc/guides/rel_notes/release_24_03.rst      |   2 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  15 ++-
 lib/ethdev/ethdev_trace.h                   |  33 ++++++
 lib/ethdev/ethdev_trace_points.c            |   9 ++
 lib/ethdev/rte_flow.c                       |  77 +++++++++++++
 lib/ethdev/rte_flow.h                       | 111 +++++++++++++++++++
 lib/ethdev/rte_flow_driver.h                |  15 +++
 lib/ethdev/version.map                      |   6 +
 12 files changed, 572 insertions(+), 6 deletions(-)
  

Comments

Thomas Monjalon Feb. 12, 2024, 2:02 p.m. UTC | #1
11/02/2024 10:30, Gregory Etelson:
> --- a/doc/guides/howto/rte_flow.rst
> +++ b/doc/guides/howto/rte_flow.rst
> +Template API resizable table
> +----------------------------
> +
> +Description
> +~~~~~~~~~~~
> +
> +A guide to the resizable template table API.

This sentence is useless. Let's keep it as short as we can.

> +
> +The resizable template table API enables applications to dynamically adjust
> +capacity of template tables without disrupting the existing flows operation.
> +The resizable template table API allows applications to optimize the memory
> +usage and performance of template tables according to the traffic conditions
> +and requirements.
> +
> +A typical use case for the resizable template table API

You may add a colon at the end of this sentence.
The below you may use "#" for automatic list numbering.

> +
> +  1. Create a resizable table with the initial capacity.
> +
> +  2. Change the table flows capacity.
> +
> +  3. Update flows that were created before the table update.
> +
> +  4. Complete the table resize procedure.
> +
> +When application begins to resize the table, it enters the resizable state.

I'm not sure to understand what means "enters the resizable state".

> +When application finishes resizing the table, it returns to the normal state.
> +Only a table in the normal state can be resized.

This sentence looks redundant with the following one.

> After a table is back to
> +the normal state, application can start a new resize.
> +Application can add, change or remove flow rules regardless of table state.
> +Table performance may worsen in the resizable state. Table performance must
> +recover after the table is back to the normal state.

This last sentence looks redundant.

> +
> +Table resize procedure must not interfere with flows that existed before
> +the table size changed.

Who the "must" is for? I suppose you are talking about driver requirement?

> +Flow handles must remain unchanged during table resize.

Again, it is for driver behaviour?

> +Application must be able to create new flows and modify or delete existing flows
> +regardless of the table state.

If it is by design we may say "Application is able".

> +
> +Application needs to set the `RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE` bit in

Please use double backquotes for code items.

> +the table attributes when creating a template table that can be resized.
> +The current API cannot make an existing table resizable if it was not created

In the guide, there is no old or current API.

> +with the `RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE` bit.

I think this sentence is redundant.

> +Resizable template table starts in the normal state.

It looks obvious.
Remember a guide is doc a detailed API description.

> +
> +Application can trigger the table to resize by calling
> +the `rte_flow_template_table_resize()` function. The resize process updates
> +the PMD table settings and port hardware to fit the new flows capacity.
> +The resize process must not affect the current flows functionality.
> +The resize process must not change the current flows handles.

Is it redundant with what is above?
Try to be concise.

> +Application can create new flows and modify or delete existing flows
> +while the table is resizing, but the table performance might be
> +slower than usual.

It was already explained before.
I prefer this one.

> +
> +Flows that existed before table resize are still functional after table resize.
> +However, the PMD flow resources that existed before table resize may not be
> +fully efficient after table resize.

What is "not fully efficient" exactly?

> In this case, application can combine
> +the old flow resources from before the resize with the new flow resources
> +from after the resize.
> +Application uses the `rte_flow_async_update_resized()` function call to update
> +flow resources. The flow update process does not interfere with or alter
> +the existing flow object. It only updates the PMD resources associated with that
> +flow.
> +The post-resize flow update process may conflict with application flows
> +operations, such as creation, removal or update. Therefore, performance-oriented
> +applications need to choose the best time to call for post-resize flow update.

Not perfectly clear. What must be done by the app exactly?

> +When application selects flows for the post table resize update, it can iterate
> +over all existing flows or it can keep track of the flows that need
> +to be updated.
> +Flows that were created after the `rte_flow_template_table_resize()`
> +call finished do not require an update.
> +
> +To return table to the normal state, use the
> +`rte_flow_template_table_resize_complete()`. If PMD does not require post-resize
> +flows

How do we know what the PMD requires?

> update and application does not care about PMD resources optimization,
> +application can avoid post-resize flows update and move resized table back to
> +the normal state right after the `rte_flow_template_table_resize()`.
> +Application can resize the table again when it is in the normal state.
> +
> +Testpmd commands:(wrapped for clarity)::

You should replace the first colon with a space.

> +
> +  # 1. Create resizable template table for 1 flow.
> +  testpmd> flow pattern_template 0 create ingress pattern_template_id 3
> +                template eth / ipv4 / udp src mask 0xffff / end
> +  testpmd> flow actions_template 0 create ingress actions_template_id 7
> +                template count  / rss / end
> +  testpmd> flow template_table 0 create table_id 101 resizable ingress
> +                group 1 priority 0 rules_number 1
> +                pattern_template 3 actions_template 7
> +
> +  # 2. Queue a flow rule.
> +  testpmd> flow queue 0 create 0 template_table 101
> +                pattern_template 0 actions_template 0 postpone no
> +                pattern eth / ipv4 / udp src spec 1 / end actions count / rss / end
> +
> +  # 3. Resize the template table
> +  #    The new table capacity is 32 rules
> +  testpmd> flow template_table 0 resize table_resize_id 101

Why not just "resize table 101" ?

> +                table_resize_rules_num 32
> +
> +  # 4. Queue more flow rules.
> +  testpmd> flow queue 0 create 0 template_table 101
> +                pattern_template 0 actions_template 0 postpone no
> +                pattern eth / ipv4 / udp src spec 2 / end actions count / rss / end
> +  testpmd> flow queue 0 create 0 template_table 101
> +                pattern_template 0 actions_template 0 postpone no
> +                pattern eth / ipv4 / udp src spec 3 / end actions count / rss / end
> +  testpmd> flow queue 0 create 0 template_table 101
> +                pattern_template 0 actions_template 0 postpone no
> +                pattern eth / ipv4 / udp src spec 4 / end actions count / rss / end
> +
> +  # 5. Queue the initial flow update.
> +  testpmd> flow queue 0 update_resized 0 rule 0
> +
> +  # 6. Complete the table resize.
> +  testpmd> flow template_table 0 resize_complete table 101
> diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst
> index 6f8ad27808..047664a079 100644
> --- a/doc/guides/rel_notes/release_24_03.rst
> +++ b/doc/guides/rel_notes/release_24_03.rst
> @@ -55,6 +55,8 @@ New Features
>       Also, make sure to start the actual text at the margin.
>       =======================================================
>  
> +* **Added support for template API table resize.**

Would be better to give a quick description to say it is adding
a new table creation flag and 4 new functions.

[...]
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> +/**
> + * Specialize table for resize.
> + */
> +#define RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE RTE_BIT32(2)
>  /**@}*/
[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query whether a table can be resized.
> + *
> + * @param port_id
> + *    Port identifier of Ethernet device.
> + * @param tbl_attr
> + *    Template table.
> + *
> + * @return
> + *   True if the table can be resized.

This is not aligned, probably because of parameters above.

> + */
> +__rte_experimental
> +bool
> +rte_flow_table_resizable(__rte_unused uint16_t port_id,
> +			 const struct rte_flow_template_table_attr *tbl_attr);
> +
[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Change template table flow rules capacity.
> + * PMD implementation must support table change to the new size.

I don't think it is the good place for this PMD comment.

> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param table
> + *   Template table to modify.
> + * @param nb_rules
> + *   New flow rules capacity.
> + * @param 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.
> + *   - (-ENOTSUP) if underlying device does not support this functionality.
> + *   - (-EINVAL) if *table* cannot be resized or resize to *nb_rules*
> + *               is not supported in PMD.
> + */
> +__rte_experimental
> +int
> +rte_flow_template_table_resize(uint16_t port_id,
> +			       struct rte_flow_template_table *table,
> +			       uint32_t nb_rules,
> +			       struct rte_flow_error *error);
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Following table resize, update flow resources in port.

You need to give details here about why, when to do it,
and what are the constraints.
It is really better to have all details in doxygen.
The RST is more to explain the global flow of the process.

> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue
> + *   Flow queue for async operation.
> + * @param attr
> + *   Async operation attributes.
> + * @param rule
> + *   Flow rule to update.
> + * @param user_data
> + *   The user data that will be returned on async completion event.
> + * @param 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.
> + *   - (-ENOTSUP) if underlying device does not support this functionality.
> + *   - (-EINVAL) if *rule* cannot be updated.
> + */
> +__rte_experimental
> +int
> +rte_flow_async_update_resized(uint16_t port_id, uint32_t queue,
> +			      const struct rte_flow_op_attr *attr,
> +			      struct rte_flow *rule, void *user_data,
> +			      struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Following table resize, notify port that all table flows were updated.

You need to give details here about when to call it.

> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param table
> + *   Template table that undergoing resize operation.
> + * @param 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.
> + *   - (-ENOTSUP) if underlying device does not support this functionality.
> + *   - (-EINVAL) PMD cannot complete table resize.
> + */
> +__rte_experimental
> +int
> +rte_flow_template_table_resize_complete(uint16_t port_id,
> +					struct rte_flow_template_table *table,
> +					struct rte_flow_error *error);
[...]
> --- a/lib/ethdev/version.map
> +++ b/lib/ethdev/version.map
> +	# added in 24.03
> +	rte_flow_table_resizable;
> +	rte_flow_template_table_resize;
> +	rte_flow_async_update_resized;
> +	rte_flow_template_table_resize_complete;

alphabetical ordering here please
  
Ferruh Yigit Feb. 12, 2024, 2:14 p.m. UTC | #2
On 2/11/2024 9:30 AM, Gregory Etelson wrote:
> Template table creation API sets table flows capacity.
> If application needs more flows then the table was designed for,
> the following procedures must be completed:
> 1. Create a new template table with larger flows capacity.
> 2. Re-create existing flows in the new table and delete flows from
>    the original table.
> 3. Destroy original table.
> 
> Application cannot always execute that procedure:
> * Port may not have sufficient resources to allocate a new table
>   while maintaining original table.
> * Application may not have existing flows "recipes" to re-create
>   flows in a new table.
> 
> The patch defines a new API that allows application to resize
> existing template table:
> 
> * Resizable template table must be created with the
> RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE_TABLE bit set.
> 
> * Application resizes existing table with the
>   `rte_flow_template_table_resize()` function call.
>   The table resize procedure updates the table maximal flow number
>   only. Other table attributes are not affected by the table resize.
>   ** The table resize procedure must not interrupt
>      existing table flows operations in hardware.
>   ** The table resize procedure must not alter flow handlers held by
>      application.
> 
> * After `rte_flow_template_table_resize()` returned, application must
>   update all existing table flow rules by calling
>   `rte_flow_async_update_resized()`.
>   The table resize procedure does not change application flow handler.
>   However, flow object can reference internal PMD resources that are
>   obsolete after table resize.
>   `rte_flow_async_update_resized()` moves internal flow references
>   to the updated table resources.
>   The flow update must not interrupt hardware flow operations.
> 
> * When all table flow were updated, application must call
>   `rte_flow_template_table_resize_complete()`.
>   The function releases PMD resources related to the original
>   table.
>   Application can start new table resize after
>   `rte_flow_template_table_resize_complete()` returned.
> 
> Testpmd commands:
> 
> * Create resizable template table
> flow template_table <port-id> create table_id <tbl-id> resizable \
>   [transfer|ingress|egres] group <group-id> \
>   rules_number <initial table capacity> \
>   pattern_template  <pt1> [ pattern_template <pt2> [ ... ]] \
>   actions_template  <at1> [ actions_template <at2> [ ... ]]
> 
> * Resize table:
> flow template_table <tbl-id> resize table_resize_id <tbl-id> \
>   table_resize_rules_num <new table capacity>
> 
> * Queue a flow update:
> flow queue <port-id> update_resized <tbl-id> rule <flow-id>
> 
> * Complete table resize:
> flow template_table <port-id> resize_complete table <tbl-id>
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>
>

Hi Gregory,

Thanks for the documentation improvement,

As the APIs are documented well and they are experimental, this enables
to properly understand and improve them when another vendor implements
them, I think this is good enough to prooceed with the APIs.

I have two more clarification requests, can you please check them below?

<...>

> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Following table resize, update flow resources in port.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue
> + *   Flow queue for async operation.
> + * @param attr
> + *   Async operation attributes.
> + * @param rule
> + *   Flow rule to update.
> + * @param user_data
> + *   The user data that will be returned on async completion event.
> + * @param 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.
> + *   - (-ENOTSUP) if underlying device does not support this functionality.
> + *   - (-EINVAL) if *rule* cannot be updated.
> + */
> +__rte_experimental
> +int
> +rte_flow_async_update_resized(uint16_t port_id, uint32_t queue,
> +			      const struct rte_flow_op_attr *attr,
> +			      struct rte_flow *rule, void *user_data,
> +			      struct rte_flow_error *error);
> +
>

If one ore more flow failed to update, for any reason, should user retry
the update (in that case we need a retry error maybe) and can user still
call 'rte_flow_template_table_resize_complete()' (for possible next
table resize operation)? Can you please clarify this in your document.


When user calls update() with a flow from new table, API should ignore
it and return success, you mentioned this is what mlx implementation is
doing, what do you think to make this as default API behavior and
document it in above API documentation?


> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Following table resize, notify port that all table flows were updated.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param table
> + *   Template table that undergoing resize operation.
> + * @param 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.
> + *   - (-ENOTSUP) if underlying device does not support this functionality.
> + *   - (-EINVAL) PMD cannot complete table resize.
> + */
> +__rte_experimental
> +int
> +rte_flow_template_table_resize_complete(uint16_t port_id,
> +					struct rte_flow_template_table *table,
> +					struct rte_flow_error *error);
>

If 'rte_flow_template_table_resize_complete()' fails, can application
call another resize()? Is this managed in the application level or
driver level (by returning error to next resize if complete() is not
successful)? Is it something to clarify in your document?
  
Gregory Etelson Feb. 12, 2024, 2:48 p.m. UTC | #3
Hello Thomas,


>> +
>> +  # 1. Create resizable template table for 1 flow.
>> +  testpmd> flow pattern_template 0 create ingress pattern_template_id 3
>> +                template eth / ipv4 / udp src mask 0xffff / end
>> +  testpmd> flow actions_template 0 create ingress actions_template_id 7
>> +                template count  / rss / end
>> +  testpmd> flow template_table 0 create table_id 101 resizable ingress
>> +                group 1 priority 0 rules_number 1
>> +                pattern_template 3 actions_template 7
>> +
>> +  # 2. Queue a flow rule.
>> +  testpmd> flow queue 0 create 0 template_table 101
>> +                pattern_template 0 actions_template 0 postpone no
>> +                pattern eth / ipv4 / udp src spec 1 / end actions count / rss / end
>> +
>> +  # 3. Resize the template table
>> +  #    The new table capacity is 32 rules
>> +  testpmd> flow template_table 0 resize table_resize_id 101
>
> Why not just "resize table 101" ?
>

That tokens pattern matched template teble creation.

I'll update the patch.
  
Gregory Etelson Feb. 12, 2024, 3:01 p.m. UTC | #4
Hello Ferruh,


>
> I have two more clarification requests, can you please check them below?
>
>
> If one ore more flow failed to update, for any reason, should user retry
> the update (in that case we need a retry error maybe) and can user still
> call 'rte_flow_template_table_resize_complete()' (for possible next
> table resize operation)? Can you please clarify this in your document.

Please see my comment below.

>
> When user calls update() with a flow from new table, API should ignore
> it and return success, you mentioned this is what mlx implementation is
> doing, what do you think to make this as default API behavior and
> document it in above API documentation?
>
>

I'll update the patch.

>
> If 'rte_flow_template_table_resize_complete()' fails, can application
> call another resize()? Is this managed in the application level or
> driver level (by returning error to next resize if complete() is not
> successful)? Is it something to clarify in your document?
>
>

PMD failures in `rte_flow_async_update_resized()` 
and `rte_flow_template_table_resize_complete()` are not recoverable.
  
Ferruh Yigit Feb. 12, 2024, 3:07 p.m. UTC | #5
On 2/12/2024 3:01 PM, Etelson, Gregory wrote:
> Hello Ferruh,
> 
> 
>>
>> I have two more clarification requests, can you please check them below?
>>
>>
>> If one ore more flow failed to update, for any reason, should user retry
>> the update (in that case we need a retry error maybe) and can user still
>> call 'rte_flow_template_table_resize_complete()' (for possible next
>> table resize operation)? Can you please clarify this in your document.
> 
> Please see my comment below.
> 
>>
>> When user calls update() with a flow from new table, API should ignore
>> it and return success, you mentioned this is what mlx implementation is
>> doing, what do you think to make this as default API behavior and
>> document it in above API documentation?
>>
>>
> 
> I'll update the patch.
> 
>>
>> If 'rte_flow_template_table_resize_complete()' fails, can application
>> call another resize()? Is this managed in the application level or
>> driver level (by returning error to next resize if complete() is not
>> successful)? Is it something to clarify in your document?
>>
>>
> 
> PMD failures in `rte_flow_async_update_resized()` and
> `rte_flow_template_table_resize_complete()` are not recoverable.
> 

OK, just please make sure this is clear in the documentation. Thanks.
  

Patch

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index ce71818705..1a2556d53b 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -134,6 +134,7 @@  enum index {
 	/* Queue arguments. */
 	QUEUE_CREATE,
 	QUEUE_DESTROY,
+	QUEUE_FLOW_UPDATE_RESIZED,
 	QUEUE_UPDATE,
 	QUEUE_AGED,
 	QUEUE_INDIRECT_ACTION,
@@ -191,8 +192,12 @@  enum index {
 	/* Table arguments. */
 	TABLE_CREATE,
 	TABLE_DESTROY,
+	TABLE_RESIZE,
+	TABLE_RESIZE_COMPLETE,
 	TABLE_CREATE_ID,
 	TABLE_DESTROY_ID,
+	TABLE_RESIZE_ID,
+	TABLE_RESIZE_RULES_NUMBER,
 	TABLE_INSERTION_TYPE,
 	TABLE_INSERTION_TYPE_NAME,
 	TABLE_HASH_FUNC,
@@ -204,6 +209,7 @@  enum index {
 	TABLE_TRANSFER,
 	TABLE_TRANSFER_WIRE_ORIG,
 	TABLE_TRANSFER_VPORT_ORIG,
+	TABLE_RESIZABLE,
 	TABLE_RULES_NUMBER,
 	TABLE_PATTERN_TEMPLATE,
 	TABLE_ACTIONS_TEMPLATE,
@@ -1323,6 +1329,8 @@  static const enum index next_group_attr[] = {
 static const enum index next_table_subcmd[] = {
 	TABLE_CREATE,
 	TABLE_DESTROY,
+	TABLE_RESIZE,
+	TABLE_RESIZE_COMPLETE,
 	ZERO,
 };
 
@@ -1337,6 +1345,7 @@  static const enum index next_table_attr[] = {
 	TABLE_TRANSFER,
 	TABLE_TRANSFER_WIRE_ORIG,
 	TABLE_TRANSFER_VPORT_ORIG,
+	TABLE_RESIZABLE,
 	TABLE_RULES_NUMBER,
 	TABLE_PATTERN_TEMPLATE,
 	TABLE_ACTIONS_TEMPLATE,
@@ -1353,6 +1362,7 @@  static const enum index next_table_destroy_attr[] = {
 static const enum index next_queue_subcmd[] = {
 	QUEUE_CREATE,
 	QUEUE_DESTROY,
+	QUEUE_FLOW_UPDATE_RESIZED,
 	QUEUE_UPDATE,
 	QUEUE_AGED,
 	QUEUE_INDIRECT_ACTION,
@@ -3344,6 +3354,19 @@  static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
 		.call = parse_table_destroy,
 	},
+	[TABLE_RESIZE] = {
+		.name = "resize",
+		.help = "resize template table",
+		.next = NEXT(NEXT_ENTRY(TABLE_RESIZE_ID)),
+		.call = parse_table
+	},
+	[TABLE_RESIZE_COMPLETE] = {
+		.name = "resize_complete",
+		.help = "complete table resize",
+		.next = NEXT(NEXT_ENTRY(TABLE_DESTROY_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, port)),
+		.call = parse_table_destroy,
+	},
 	/* Table  arguments. */
 	[TABLE_CREATE_ID] = {
 		.name = "table_id",
@@ -3354,13 +3377,29 @@  static const struct token token_list[] = {
 	},
 	[TABLE_DESTROY_ID] = {
 		.name = "table",
-		.help = "specify table id to destroy",
+		.help = "table id",
 		.next = NEXT(next_table_destroy_attr,
 			     NEXT_ENTRY(COMMON_TABLE_ID)),
 		.args = ARGS(ARGS_ENTRY_PTR(struct buffer,
 					    args.table_destroy.table_id)),
 		.call = parse_table_destroy,
 	},
+	[TABLE_RESIZE_ID] = {
+		.name = "table_resize_id",
+		.help = "table resize id",
+		.next = NEXT(NEXT_ENTRY(TABLE_RESIZE_RULES_NUMBER),
+			     NEXT_ENTRY(COMMON_TABLE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.table.id)),
+		.call = parse_table
+	},
+	[TABLE_RESIZE_RULES_NUMBER] = {
+		.name = "table_resize_rules_num",
+		.help = "table resize rules number",
+		.next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+					args.table.attr.nb_flows)),
+		.call = parse_table
+	},
 	[TABLE_INSERTION_TYPE] = {
 		.name = "insertion_type",
 		.help = "specify insertion type",
@@ -3433,6 +3472,12 @@  static const struct token token_list[] = {
 		.next = NEXT(next_table_attr),
 		.call = parse_table,
 	},
+	[TABLE_RESIZABLE] = {
+		.name = "resizable",
+		.help = "set resizable attribute",
+		.next = NEXT(next_table_attr),
+		.call = parse_table,
+	},
 	[TABLE_RULES_NUMBER] = {
 		.name = "rules_number",
 		.help = "number of rules in table",
@@ -3525,6 +3570,14 @@  static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, queue)),
 		.call = parse_qo_destroy,
 	},
+	[QUEUE_FLOW_UPDATE_RESIZED] = {
+		.name = "update_resized",
+		.help = "update a flow after table resize",
+		.next = NEXT(NEXT_ENTRY(QUEUE_DESTROY_ID),
+			     NEXT_ENTRY(COMMON_QUEUE_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+		.call = parse_qo_destroy,
+	},
 	[QUEUE_UPDATE] = {
 		.name = "update",
 		.help = "update a flow rule",
@@ -10334,6 +10387,7 @@  parse_table(struct context *ctx, const struct token *token,
 	}
 	switch (ctx->curr) {
 	case TABLE_CREATE:
+	case TABLE_RESIZE:
 		out->command = ctx->curr;
 		ctx->objdata = 0;
 		ctx->object = out;
@@ -10378,18 +10432,25 @@  parse_table(struct context *ctx, const struct token *token,
 	case TABLE_TRANSFER_WIRE_ORIG:
 		if (!out->args.table.attr.flow_attr.transfer)
 			return -1;
-		out->args.table.attr.specialize = RTE_FLOW_TABLE_SPECIALIZE_TRANSFER_WIRE_ORIG;
+		out->args.table.attr.specialize |= RTE_FLOW_TABLE_SPECIALIZE_TRANSFER_WIRE_ORIG;
 		return len;
 	case TABLE_TRANSFER_VPORT_ORIG:
 		if (!out->args.table.attr.flow_attr.transfer)
 			return -1;
-		out->args.table.attr.specialize = RTE_FLOW_TABLE_SPECIALIZE_TRANSFER_VPORT_ORIG;
+		out->args.table.attr.specialize |= RTE_FLOW_TABLE_SPECIALIZE_TRANSFER_VPORT_ORIG;
+		return len;
+	case TABLE_RESIZABLE:
+		out->args.table.attr.specialize |=
+			RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE;
 		return len;
 	case TABLE_RULES_NUMBER:
 		ctx->objdata = 0;
 		ctx->object = out;
 		ctx->objmask = NULL;
 		return len;
+	case TABLE_RESIZE_ID:
+	case TABLE_RESIZE_RULES_NUMBER:
+		return len;
 	default:
 		return -1;
 	}
@@ -10411,7 +10472,8 @@  parse_table_destroy(struct context *ctx, const struct token *token,
 	if (!out)
 		return len;
 	if (!out->command || out->command == TABLE) {
-		if (ctx->curr != TABLE_DESTROY)
+		if (ctx->curr != TABLE_DESTROY &&
+		    ctx->curr != TABLE_RESIZE_COMPLETE)
 			return -1;
 		if (sizeof(*out) > size)
 			return -1;
@@ -10513,7 +10575,8 @@  parse_qo_destroy(struct context *ctx, const struct token *token,
 	if (!out)
 		return len;
 	if (!out->command || out->command == QUEUE) {
-		if (ctx->curr != QUEUE_DESTROY)
+		if (ctx->curr != QUEUE_DESTROY &&
+		    ctx->curr != QUEUE_FLOW_UPDATE_RESIZED)
 			return -1;
 		if (sizeof(*out) > size)
 			return -1;
@@ -12569,10 +12632,18 @@  cmd_flow_parsed(const struct buffer *in)
 					in->args.table_destroy.table_id_n,
 					in->args.table_destroy.table_id);
 		break;
+	case TABLE_RESIZE_COMPLETE:
+		port_flow_template_table_resize_complete
+			(in->port, in->args.table_destroy.table_id[0]);
+		break;
 	case GROUP_SET_MISS_ACTIONS:
 		port_queue_group_set_miss_actions(in->port, &in->args.vc.attr,
 						  in->args.vc.actions);
 		break;
+	case TABLE_RESIZE:
+		port_flow_template_table_resize(in->port, in->args.table.id,
+						in->args.table.attr.nb_flows);
+		break;
 	case QUEUE_CREATE:
 		port_queue_flow_create(in->port, in->queue, in->postpone,
 			in->args.vc.table_id, in->args.vc.rule_id,
@@ -12584,6 +12655,11 @@  cmd_flow_parsed(const struct buffer *in)
 					in->args.destroy.rule_n,
 					in->args.destroy.rule);
 		break;
+	case QUEUE_FLOW_UPDATE_RESIZED:
+		port_queue_flow_update_resized(in->port, in->queue,
+					       in->postpone,
+					       in->args.destroy.rule[0]);
+		break;
 	case QUEUE_UPDATE:
 		port_queue_flow_update(in->port, in->queue, in->postpone,
 				in->args.vc.rule_id, in->args.vc.act_templ_id,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index cad7537bc6..e589ac614b 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1403,6 +1403,19 @@  port_flow_new(const struct rte_flow_attr *attr,
 	return NULL;
 }
 
+static struct port_flow *
+port_flow_locate(struct port_flow *flows_list, uint32_t flow_id)
+{
+	struct port_flow *pf = flows_list;
+
+	while (pf) {
+		if (pf->id == flow_id)
+			break;
+		pf = pf->next;
+	}
+	return pf;
+}
+
 /** Print a message out of a flow error. */
 static int
 port_flow_complain(struct rte_flow_error *error)
@@ -1693,6 +1706,19 @@  table_alloc(uint32_t id, struct port_table **table,
 	return 0;
 }
 
+static struct port_table *
+port_table_locate(struct port_table *tables_list, uint32_t table_id)
+{
+	struct port_table *pt = tables_list;
+
+	while (pt) {
+		if (pt->id == table_id)
+			break;
+		pt = pt->next;
+	}
+	return pt;
+}
+
 /** Get info about flow management resources. */
 int
 port_flow_get_info(portid_t port_id)
@@ -2665,6 +2691,46 @@  port_flow_template_table_destroy(portid_t port_id,
 	return ret;
 }
 
+int
+port_flow_template_table_resize_complete(portid_t port_id, uint32_t table_id)
+{
+	struct rte_port *port;
+	struct port_table *pt;
+	struct rte_flow_error error = { 0, };
+	int ret;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN))
+		return -EINVAL;
+	port = &ports[port_id];
+	pt = port_table_locate(port->table_list, table_id);
+	if (!pt)
+		return -EINVAL;
+	ret = rte_flow_template_table_resize_complete(port_id,
+						      pt->table, &error);
+	return !ret ? 0 : port_flow_complain(&error);
+}
+
+int
+port_flow_template_table_resize(portid_t port_id,
+				uint32_t table_id, uint32_t flows_num)
+{
+	struct rte_port *port;
+	struct port_table *pt;
+	struct rte_flow_error error = { 0, };
+	int ret;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN))
+		return -EINVAL;
+	port = &ports[port_id];
+	pt = port_table_locate(port->table_list, table_id);
+	if (!pt)
+		return -EINVAL;
+	ret = rte_flow_template_table_resize(port_id, pt->table, flows_num, &error);
+	if (ret)
+		return port_flow_complain(&error);
+	return 0;
+}
+
 /** Flush table */
 int
 port_flow_template_table_flush(portid_t port_id)
@@ -2805,6 +2871,42 @@  port_queue_flow_create(portid_t port_id, queueid_t queue_id,
 	return 0;
 }
 
+int
+port_queue_flow_update_resized(portid_t port_id, queueid_t queue_id,
+			       bool postpone, uint32_t flow_id)
+{
+	const struct rte_flow_op_attr op_attr = { .postpone = postpone };
+	struct rte_flow_error error = { 0, };
+	struct port_flow *pf;
+	struct rte_port *port;
+	struct queue_job *job;
+	int ret;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN) ||
+	    port_id == (portid_t)RTE_PORT_ALL)
+		return -EINVAL;
+	port = &ports[port_id];
+	if (queue_id >= port->queue_nb) {
+		printf("Queue #%u is invalid\n", queue_id);
+		return -EINVAL;
+	}
+	pf = port_flow_locate(port->flow_list, flow_id);
+	if (!pf)
+		return -EINVAL;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return -ENOMEM;
+	job->type = QUEUE_JOB_TYPE_FLOW_TRANSFER;
+	job->pf = pf;
+	ret = rte_flow_async_update_resized(port_id, queue_id, &op_attr,
+					    pf->flow, job, &error);
+	if (ret) {
+		free(job);
+		return port_flow_complain(&error);
+	}
+	return 0;
+}
+
 /** Enqueue number of destroy flow rules operations. */
 int
 port_queue_flow_destroy(portid_t port_id, queueid_t queue_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 9b10a9ea1c..92f21e7776 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -110,6 +110,7 @@  enum {
 enum {
 	QUEUE_JOB_TYPE_FLOW_CREATE,
 	QUEUE_JOB_TYPE_FLOW_DESTROY,
+	QUEUE_JOB_TYPE_FLOW_TRANSFER,
 	QUEUE_JOB_TYPE_FLOW_UPDATE,
 	QUEUE_JOB_TYPE_ACTION_CREATE,
 	QUEUE_JOB_TYPE_ACTION_DESTROY,
@@ -981,7 +982,12 @@  int port_flow_template_table_create(portid_t port_id, uint32_t id,
 		   uint32_t nb_actions_templates, uint32_t *actions_templates);
 int port_flow_template_table_destroy(portid_t port_id,
 			    uint32_t n, const uint32_t *table);
+int port_queue_flow_update_resized(portid_t port_id, queueid_t queue_id,
+				   bool postpone, uint32_t flow_id);
 int port_flow_template_table_flush(portid_t port_id);
+int port_flow_template_table_resize_complete(portid_t port_id, uint32_t table_id);
+int port_flow_template_table_resize(portid_t port_id,
+				    uint32_t table_id, uint32_t flows_num);
 int port_queue_group_set_miss_actions(portid_t port_id, const struct rte_flow_attr *attr,
 				      const struct rte_flow_action *actions);
 int port_queue_flow_create(portid_t port_id, queueid_t queue_id,
diff --git a/doc/guides/howto/rte_flow.rst b/doc/guides/howto/rte_flow.rst
index 27d4f28f77..7cdaadd0ed 100644
--- a/doc/guides/howto/rte_flow.rst
+++ b/doc/guides/howto/rte_flow.rst
@@ -303,3 +303,119 @@  Terminal 1: output log::
   received packet with src ip = 176.80.50.4 sent to queue 3
   received packet with src ip = 176.80.50.5 sent to queue 1
   received packet with src ip = 176.80.50.6 sent to queue 3
+
+Template API resizable table
+----------------------------
+
+Description
+~~~~~~~~~~~
+
+A guide to the resizable template table API.
+
+The resizable template table API enables applications to dynamically adjust
+capacity of template tables without disrupting the existing flows operation.
+The resizable template table API allows applications to optimize the memory
+usage and performance of template tables according to the traffic conditions
+and requirements.
+
+A typical use case for the resizable template table API
+
+  1. Create a resizable table with the initial capacity.
+
+  2. Change the table flows capacity.
+
+  3. Update flows that were created before the table update.
+
+  4. Complete the table resize procedure.
+
+When application begins to resize the table, it enters the resizable state.
+When application finishes resizing the table, it returns to the normal state.
+Only a table in the normal state can be resized. After a table is back to
+the normal state, application can start a new resize.
+Application can add, change or remove flow rules regardless of table state.
+Table performance may worsen in the resizable state. Table performance must
+recover after the table is back to the normal state.
+
+Table resize procedure must not interfere with flows that existed before
+the table size changed.
+Flow handles must remain unchanged during table resize.
+Application must be able to create new flows and modify or delete existing flows
+regardless of the table state.
+
+Application needs to set the `RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE` bit in
+the table attributes when creating a template table that can be resized.
+The current API cannot make an existing table resizable if it was not created
+with the `RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE` bit.
+Resizable template table starts in the normal state.
+
+Application can trigger the table to resize by calling
+the `rte_flow_template_table_resize()` function. The resize process updates
+the PMD table settings and port hardware to fit the new flows capacity.
+The resize process must not affect the current flows functionality.
+The resize process must not change the current flows handles.
+Application can create new flows and modify or delete existing flows
+while the table is resizing, but the table performance might be
+slower than usual.
+
+Flows that existed before table resize are still functional after table resize.
+However, the PMD flow resources that existed before table resize may not be
+fully efficient after table resize. In this case, application can combine
+the old flow resources from before the resize with the new flow resources
+from after the resize.
+Application uses the `rte_flow_async_update_resized()` function call to update
+flow resources. The flow update process does not interfere with or alter
+the existing flow object. It only updates the PMD resources associated with that
+flow.
+The post-resize flow update process may conflict with application flows
+operations, such as creation, removal or update. Therefore, performance-oriented
+applications need to choose the best time to call for post-resize flow update.
+When application selects flows for the post table resize update, it can iterate
+over all existing flows or it can keep track of the flows that need
+to be updated.
+Flows that were created after the `rte_flow_template_table_resize()`
+call finished do not require an update.
+
+To return table to the normal state, use the
+`rte_flow_template_table_resize_complete()`. If PMD does not require post-resize
+flows update and application does not care about PMD resources optimization,
+application can avoid post-resize flows update and move resized table back to
+the normal state right after the `rte_flow_template_table_resize()`.
+Application can resize the table again when it is in the normal state.
+
+Testpmd commands:(wrapped for clarity)::
+
+  # 1. Create resizable template table for 1 flow.
+  testpmd> flow pattern_template 0 create ingress pattern_template_id 3
+                template eth / ipv4 / udp src mask 0xffff / end
+  testpmd> flow actions_template 0 create ingress actions_template_id 7
+                template count  / rss / end
+  testpmd> flow template_table 0 create table_id 101 resizable ingress
+                group 1 priority 0 rules_number 1
+                pattern_template 3 actions_template 7
+
+  # 2. Queue a flow rule.
+  testpmd> flow queue 0 create 0 template_table 101
+                pattern_template 0 actions_template 0 postpone no
+                pattern eth / ipv4 / udp src spec 1 / end actions count / rss / end
+
+  # 3. Resize the template table
+  #    The new table capacity is 32 rules
+  testpmd> flow template_table 0 resize table_resize_id 101
+                table_resize_rules_num 32
+
+  # 4. Queue more flow rules.
+  testpmd> flow queue 0 create 0 template_table 101
+                pattern_template 0 actions_template 0 postpone no
+                pattern eth / ipv4 / udp src spec 2 / end actions count / rss / end
+  testpmd> flow queue 0 create 0 template_table 101
+                pattern_template 0 actions_template 0 postpone no
+                pattern eth / ipv4 / udp src spec 3 / end actions count / rss / end
+  testpmd> flow queue 0 create 0 template_table 101
+                pattern_template 0 actions_template 0 postpone no
+                pattern eth / ipv4 / udp src spec 4 / end actions count / rss / end
+
+  # 5. Queue the initial flow update.
+  testpmd> flow queue 0 update_resized 0 rule 0
+
+  # 6. Complete the table resize.
+  testpmd> flow template_table 0 resize_complete table 101
diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst
index 6f8ad27808..047664a079 100644
--- a/doc/guides/rel_notes/release_24_03.rst
+++ b/doc/guides/rel_notes/release_24_03.rst
@@ -55,6 +55,8 @@  New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added support for template API table resize.**
+
 
 Removed Items
 -------------
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 447e28e694..d0d3adf643 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -2984,12 +2984,21 @@  following sections.
 - Create a table::
 
    flow table {port_id} create
-       [table_id {id}]
+       [table_id {id}] [resizable]
        [group {group_id}] [priority {level}] [ingress] [egress] [transfer]
        rules_number {number}
        pattern_template {pattern_template_id}
        actions_template {actions_template_id}
 
+- Resize a table::
+
+   flow template_table {port_id} resize
+       table_resize_id {id} table_resize_rules_num {number}
+
+- Complete table resize::
+
+   flow template_table {port_id} resize_complete table {table_id}
+
 - Destroy a table::
 
    flow table {port_id} destroy table {id} [...]
@@ -3010,6 +3019,10 @@  following sections.
        pattern {item} [/ {item} [...]] / end
        actions {action} [/ {action} [...]] / end
 
+- Enqueue flow update following table resize::
+
+   flow queue {port_id} update_resized {table_id} rule {rule_id}
+
 - Enqueue destruction of specific flow rules::
 
    flow queue {port_id} destroy {queue_id}
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 1b1ae0cfe8..cd3327a619 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2572,6 +2572,39 @@  RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_ptr(user_data);
 	rte_trace_point_emit_int(ret);
 )
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_template_table_resize,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id,
+			     struct rte_flow_template_table *table,
+			     uint32_t nb_rules, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_ptr(table);
+	rte_trace_point_emit_u32(nb_rules);
+	rte_trace_point_emit_int(ret);
+)
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_async_update_resized,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id, uint32_t queue,
+			     const struct rte_flow_op_attr *attr,
+			     struct rte_flow *rule, void *user_data, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_u32(queue);
+	rte_trace_point_emit_ptr(attr);
+	rte_trace_point_emit_ptr(rule);
+	rte_trace_point_emit_ptr(user_data);
+	rte_trace_point_emit_int(ret);
+)
+
+RTE_TRACE_POINT_FP(
+	rte_flow_trace_table_resize_complete,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id,
+			     struct rte_flow_template_table *table, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_ptr(table);
+	rte_trace_point_emit_int(ret);
+)
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 91f71d868b..1a1f685daa 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -774,3 +774,12 @@  RTE_TRACE_POINT_REGISTER(rte_flow_trace_async_action_list_handle_destroy,
 
 RTE_TRACE_POINT_REGISTER(rte_flow_trace_async_action_list_handle_query_update,
 			 lib.ethdev.flow.async_action_list_handle_query_update)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_template_table_resize,
+			 lib.ethdev.flow.template_table_resize)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_async_update_resized,
+			 lib.ethdev.flow.async_update_resized)
+
+RTE_TRACE_POINT_REGISTER(rte_flow_trace_table_resize_complete,
+			 lib.ethdev.flow.table_resize_complete)
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index f49d1d3767..99987e6518 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -2481,3 +2481,80 @@  rte_flow_calc_table_hash(uint16_t port_id, const struct rte_flow_template_table
 					hash, error);
 	return flow_err(port_id, ret, error);
 }
+
+bool
+rte_flow_table_resizable(__rte_unused uint16_t port_id,
+			 const struct rte_flow_template_table_attr *tbl_attr)
+{
+	return (tbl_attr->specialize &
+		RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE) != 0;
+}
+
+int
+rte_flow_template_table_resize(uint16_t port_id,
+			       struct rte_flow_template_table *table,
+			       uint32_t nb_rules,
+			       struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->flow_template_table_resize)
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "flow_template_table_resize not supported");
+	dev = &rte_eth_devices[port_id];
+	ret = ops->flow_template_table_resize(dev, table, nb_rules, error);
+	ret = flow_err(port_id, ret, error);
+	rte_flow_trace_template_table_resize(port_id, table, nb_rules, ret);
+	return ret;
+}
+
+int
+rte_flow_async_update_resized(uint16_t port_id, uint32_t queue,
+			      const struct rte_flow_op_attr *attr,
+			      struct rte_flow *rule, void *user_data,
+			      struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->flow_update_resized)
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "async_flow_async_transfer not supported");
+	dev = &rte_eth_devices[port_id];
+	ret = ops->flow_update_resized(dev, queue, attr, rule, user_data, error);
+	ret = flow_err(port_id, ret, error);
+	rte_flow_trace_async_update_resized(port_id, queue, attr,
+					    rule, user_data, ret);
+	return ret;
+}
+
+int
+rte_flow_template_table_resize_complete(uint16_t port_id,
+					struct rte_flow_template_table *table,
+					struct rte_flow_error *error)
+{
+	int ret;
+	struct rte_eth_dev *dev;
+	const struct rte_flow_ops *ops;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->flow_template_table_resize_complete)
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "flow_template_table_transfer_complete not supported");
+	dev = &rte_eth_devices[port_id];
+	ret = ops->flow_template_table_resize_complete(dev, table, error);
+	ret = flow_err(port_id, ret, error);
+	rte_flow_trace_table_resize_complete(port_id, table, ret);
+	return ret;
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 78b6bbb159..9aedc06516 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5746,6 +5746,10 @@  struct rte_flow_template_table;
  * if the hint is supported.
  */
 #define RTE_FLOW_TABLE_SPECIALIZE_TRANSFER_VPORT_ORIG RTE_BIT32(1)
+/**
+ * Specialize table for resize.
+ */
+#define RTE_FLOW_TABLE_SPECIALIZE_RESIZABLE RTE_BIT32(2)
 /**@}*/
 
 /**
@@ -5824,6 +5828,25 @@  struct rte_flow_template_table_attr {
 	enum rte_flow_table_hash_func hash_func;
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query whether a table can be resized.
+ *
+ * @param port_id
+ *    Port identifier of Ethernet device.
+ * @param tbl_attr
+ *    Template table.
+ *
+ * @return
+ *   True if the table can be resized.
+ */
+__rte_experimental
+bool
+rte_flow_table_resizable(__rte_unused uint16_t port_id,
+			 const struct rte_flow_template_table_attr *tbl_attr);
+
 /**
  * @warning
  * @b EXPERIMENTAL: this API may change without prior notice.
@@ -6750,6 +6773,94 @@  rte_flow_calc_table_hash(uint16_t port_id, const struct rte_flow_template_table
 			 const struct rte_flow_item pattern[], uint8_t pattern_template_index,
 			 uint32_t *hash, struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Change template table flow rules capacity.
+ * PMD implementation must support table change to the new size.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param table
+ *   Template table to modify.
+ * @param nb_rules
+ *   New flow rules capacity.
+ * @param 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.
+ *   - (-ENOTSUP) if underlying device does not support this functionality.
+ *   - (-EINVAL) if *table* cannot be resized or resize to *nb_rules*
+ *               is not supported in PMD.
+ */
+__rte_experimental
+int
+rte_flow_template_table_resize(uint16_t port_id,
+			       struct rte_flow_template_table *table,
+			       uint32_t nb_rules,
+			       struct rte_flow_error *error);
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Following table resize, update flow resources in port.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue
+ *   Flow queue for async operation.
+ * @param attr
+ *   Async operation attributes.
+ * @param rule
+ *   Flow rule to update.
+ * @param user_data
+ *   The user data that will be returned on async completion event.
+ * @param 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.
+ *   - (-ENOTSUP) if underlying device does not support this functionality.
+ *   - (-EINVAL) if *rule* cannot be updated.
+ */
+__rte_experimental
+int
+rte_flow_async_update_resized(uint16_t port_id, uint32_t queue,
+			      const struct rte_flow_op_attr *attr,
+			      struct rte_flow *rule, void *user_data,
+			      struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Following table resize, notify port that all table flows were updated.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param table
+ *   Template table that undergoing resize operation.
+ * @param 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.
+ *   - (-ENOTSUP) if underlying device does not support this functionality.
+ *   - (-EINVAL) PMD cannot complete table resize.
+ */
+__rte_experimental
+int
+rte_flow_template_table_resize_complete(uint16_t port_id,
+					struct rte_flow_template_table *table,
+					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 f35f659503..53d9393575 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -370,6 +370,21 @@  struct rte_flow_ops {
 		(struct rte_eth_dev *dev, const struct rte_flow_template_table *table,
 		 const struct rte_flow_item pattern[], uint8_t pattern_template_index,
 		 uint32_t *hash, struct rte_flow_error *error);
+	/** @see rte_flow_template_table_resize() */
+	int (*flow_template_table_resize)(struct rte_eth_dev *dev,
+					  struct rte_flow_template_table *table,
+					  uint32_t nb_rules,
+					  struct rte_flow_error *error);
+	/** @see rte_flow_async_update_resized() */
+	int (*flow_update_resized)(struct rte_eth_dev *dev, uint32_t queue,
+				   const struct rte_flow_op_attr *attr,
+				   struct rte_flow *rule, void *user_data,
+				   struct rte_flow_error *error);
+	/** @see rte_flow_template_table_resize_complete() */
+	int (*flow_template_table_resize_complete)
+			(struct rte_eth_dev *dev,
+			 struct rte_flow_template_table *table,
+			 struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 5c4917c020..5b22aa598a 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -316,6 +316,12 @@  EXPERIMENTAL {
 	rte_eth_recycle_rx_queue_info_get;
 	rte_flow_group_set_miss_actions;
 	rte_flow_calc_table_hash;
+
+	# added in 24.03
+	rte_flow_table_resizable;
+	rte_flow_template_table_resize;
+	rte_flow_async_update_resized;
+	rte_flow_template_table_resize_complete;
 };
 
 INTERNAL {