All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] ethdev: add query_update sync and async function calls
@ 2022-12-21  7:35 Gregory Etelson
  2022-12-21  7:35 ` [PATCH 2/2] ethdev: add quota flow action and item Gregory Etelson
                   ` (10 more replies)
  0 siblings, 11 replies; 58+ messages in thread
From: Gregory Etelson @ 2022-12-21  7:35 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If port hardware allows both update and query in a single operation,
application still has to issue 2 separate hardware requests.

The patch adds `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

int
rte_flow_action_handle_query_update
       (uint16_t port_id, struct rte_flow_action_handle *handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        struct rte_flow_error *error);

int
rte_flow_async_action_handle_query_update
       (uint16_t port_id, uint32_t queue_id,
        const struct rte_flow_op_attr *op_attr,
        struct rte_flow_action_handle *action_handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        void *user_data, struct rte_flow_error *error);

Application can control query and update order, if that is supported
by port hardware, by setting qu_mode parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values provide
query only and update only functionality for backward compatibility
with existing API.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
 lib/ethdev/rte_flow.c        |  39 +++++++++++++
 lib/ethdev/rte_flow.h        | 105 +++++++++++++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h |  15 +++++
 lib/ethdev/version.map       |   5 ++
 4 files changed, 164 insertions(+)

diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..8b8aa940be 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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 (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..f9e919bb80 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,111 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query_update operational mode.
+ *
+ * RTE_FLOW_QU_DEFAULT
+ *   Default query_update operational mode.
+ *   If both `update` and `query` parameters are not NULL the call updates and
+ *   queries action in default port order.
+ *   If `update` parameter is NULL the call queries action.
+ *   If `query` parameter is NULL the call updates action.
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before update.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_DEFAULT,      /* HW default mode  */
+	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
+	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] handle
+ *   Handle for the indirect action object to be updated.
+ * @param update[in]
+ *   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] query
+ *   Pointer to storage for the associated query data type.
+ * @param[in] mode
+ *   Operational mode.
+ * @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_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue which is used to update the rule.
+ * @param[in] attr
+ *   Indirect action update operation attributes.
+ * @param[in] 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[in] query
+ *   Pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param[in] mode
+ *   Operational mode.
+ * @param[in] user_data
+ *   The user data that will be returned on async completion event.
+ * @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_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..42f0d7b30c 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# future
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH 2/2] ethdev: add quota flow action and item
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
@ 2022-12-21  7:35 ` Gregory Etelson
  2023-01-08 13:47   ` Ori Kam
  2023-01-04  9:56 ` [PATCH 1/2] ethdev: add query_update sync and async function calls Ori Kam
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2022-12-21  7:35 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Viacheslav Ovsiienko, Thomas Monjalon,
	Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
 app/test-pmd/cmdline_flow.c          | 395 ++++++++++++++++++++++++++-
 app/test-pmd/config.c                |  82 +++++-
 app/test-pmd/testpmd.h               |  11 +
 doc/guides/nics/features/default.ini |   2 +
 doc/guides/nics/features/mlx5.ini    |   2 +
 doc/guides/nics/mlx5.rst             |  12 +
 doc/guides/prog_guide/rte_flow.rst   |  41 +++
 lib/ethdev/rte_flow.c                |   2 +
 lib/ethdev/rte_flow.h                | 123 +++++++++
 9 files changed, 660 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..5407a72ee2 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,108 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_DEFAULT] = "default",
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10668,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10697,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11091,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11122,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/nics/features/mlx5.ini b/doc/guides/nics/features/mlx5.ini
index 62fd330e2b..ff9ea0cb43 100644
--- a/doc/guides/nics/features/mlx5.ini
+++ b/doc/guides/nics/features/mlx5.ini
@@ -80,6 +80,7 @@ mpls                 = Y
 nvgre                = Y
 port_id              = Y
 port_representor     = Y
+quota                = Y
 tag                  = Y
 tcp                  = Y
 udp                  = Y
@@ -112,6 +113,7 @@ of_set_vlan_pcp      = Y
 of_set_vlan_vid      = Y
 port_id              = Y
 queue                = Y
+quota                = Y
 raw_decap            = Y
 raw_encap            = Y
 represented_port     = Y
diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst
index 51f51259e3..1aac8becf2 100644
--- a/doc/guides/nics/mlx5.rst
+++ b/doc/guides/nics/mlx5.rst
@@ -291,6 +291,18 @@ Limitations
 - No Tx metadata go to the E-Switch steering domain for the Flow group 0.
   The flows within group 0 and set metadata action are rejected by hardware.
 
+- Quota:
+
+  - Template API only (HWS).
+  - Quota flow action and item supported in non-root HWS tables – flow group must be > 0.
+  - Quota implemented as indirect flow action only.
+  - Maximal value for quota SET and ADD operations in INT32_MAX (2G)
+  - Application cannot use 2 consecutive ADD updates.
+    Next tokens update after ADD must always be SET.
+  - HW can reduce non-negative quota to negative value.
+  - Quota flow action cannot be used with Meter or CT flow actions in the same rule.
+  - Maximal number of HW quota and HW meter objects <= 16e6.
+
 .. note::
 
    MAC addresses not already present in the bridge table of the associated
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 8b8aa940be..6439de3c1d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index f9e919bb80..c67f7c0203 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply quota verdict - PASS or BLOCK to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /** quota operational mode */
+	int64_t quota;                 /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /** update operation */
+	int64_t quota;                    /** quota value */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* RE: [PATCH 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
  2022-12-21  7:35 ` [PATCH 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-01-04  9:56 ` Ori Kam
  2023-01-11  9:28   ` Gregory Etelson
  2023-01-11  9:22 ` [PATCH v2 " Gregory Etelson
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 58+ messages in thread
From: Ori Kam @ 2023-01-04  9:56 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: Matan Azrad, Raslan Darawsheh,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit, Andrew Rybchenko

Hi Gregory,

Some comments below,
Also you are missing the release rst file update.

> -----Original Message-----
> From: Gregory Etelson <getelson@nvidia.com>
> Sent: Wednesday, 21 December 2022 9:36
> 
> Current API allows either query or update indirect flow action.
> If port hardware allows both update and query in a single operation,
> application still has to issue 2 separate hardware requests.
> 
> The patch adds `rte_flow_action_handle_query_update` function call,
> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.
> 
> int
> rte_flow_action_handle_query_update
>        (uint16_t port_id, struct rte_flow_action_handle *handle,
>         const void *update, void *query,
>         enum rte_flow_query_update_mode mode,
>         struct rte_flow_error *error);
> 
> int
> rte_flow_async_action_handle_query_update
>        (uint16_t port_id, uint32_t queue_id,
>         const struct rte_flow_op_attr *op_attr,
>         struct rte_flow_action_handle *action_handle,
>         const void *update, void *query,
>         enum rte_flow_query_update_mode mode,
>         void *user_data, struct rte_flow_error *error);
> 
> Application can control query and update order, if that is supported
> by port hardware, by setting qu_mode parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values
> provide
> query only and update only functionality for backward compatibility
> with existing API.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> ---
>  lib/ethdev/rte_flow.c        |  39 +++++++++++++
>  lib/ethdev/rte_flow.h        | 105 +++++++++++++++++++++++++++++++++++
>  lib/ethdev/rte_flow_driver.h |  15 +++++
>  lib/ethdev/version.map       |   5 ++
>  4 files changed, 164 insertions(+)
> 
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> index 7d0c24366c..8b8aa940be 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t
> port_id,
>  					  action_handle, data, user_data,
> error);
>  	return flow_err(port_id, ret, error);
>  }
> +
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode
> mode,
> +				    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 (!ops || !ops->action_handle_query_update)
> +		return -ENOTSUP;
> +	ret = ops->action_handle_query_update(dev, handle, update, query,
> +					      mode, error);
> +	return flow_err(port_id, ret, error);
> +}
> +
> +int
> +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle
> *handle,
> +					  const void *update, void *query,
> +					  enum
> rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  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 (!ops || !ops->async_action_handle_query_update)
> +		return -ENOTSUP;
> +	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
> +						    handle, update, query,
> mode,
> +						    user_data, error);
> +	return flow_err(port_id, ret, error);
> +}
> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index b60987db4b..f9e919bb80 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -5622,6 +5622,111 @@ rte_flow_async_action_handle_query(uint16_t
> port_id,
>  		void *user_data,
>  		struct rte_flow_error *error);
> 
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query_update operational mode.
> + *
> + * RTE_FLOW_QU_DEFAULT
> + *   Default query_update operational mode.
> + *   If both `update` and `query` parameters are not NULL the call updates
> and

I don't think the above line is relevant.

> + *   queries action in default port order.
> + *   If `update` parameter is NULL the call queries action.
> + *   If `query` parameter is NULL the call updates action.
> + * RTE_FLOW_QU_QUERY_FIRST
> + *   Force port to query action before update.
> + * RTE_FLOW_QU_UPDATE_FIRST
> + *   Force port to update action before update.
> + *
> + * @see rte_flow_action_handle_query_update()
> + * @see rte_flow_async_action_handle_query_update()
> + */
> +enum rte_flow_query_update_mode {
> +	RTE_FLOW_QU_DEFAULT,      /* HW default mode  */

I don't think this is valid. Since if the application is not aware of what is the default
it can't assume anything about the query value.
If we keep it means that each PMD must document what is the default.

> +	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
> +	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and/or update indirect flow action.

I think better wording will be:
Atomic query and update.
This function can also be used just for query/update when the not needed
field is NULL.

@see rte_flow_action_handle_update.
@see rte_flow_action_handle_query

> + *
> + * @param[in] port_id
> + *   Port identifier of Ethernet device.
> + * @param[in] handle
> + *   Handle for the indirect action object to be updated.
> + * @param update[in]
> + *   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] query
> + *   Pointer to storage for the associated query data type.
> + * @param[in] mode
> + *   Operational mode.
> + * @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_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode
> mode,
> +				    struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue async indirect flow action query and/or update
> + *

Same comments as above.

> + * @param[in] port_id
> + *   Port identifier of Ethernet device.
> + * @param[in] queue_id
> + *   Flow queue which is used to update the rule.
> + * @param[in] attr
> + *   Indirect action update operation attributes.
> + * @param[in] 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[in] query
> + *   Pointer to storage for the associated query data type.
> + *   Query result returned on async completion event.
> + * @param[in] mode
> + *   Operational mode.
> + * @param[in] user_data
> + *   The user data that will be returned on async completion event.
> + * @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_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle
> *handle,
> +					  const void *update, void *query,
> +					  enum
> rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  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 c7d0699c91..7358c10a7a 100644
> --- a/lib/ethdev/rte_flow_driver.h
> +++ b/lib/ethdev/rte_flow_driver.h
> @@ -114,6 +114,13 @@ struct rte_flow_ops {
>  		 const struct rte_flow_action_handle *handle,
>  		 void *data,
>  		 struct rte_flow_error *error);
> +	/** See rte_flow_action_handle_query_update() */
> +	int (*action_handle_query_update)
> +		(struct rte_eth_dev *dev,
> +		 struct rte_flow_action_handle *handle,
> +		 const void *update, void *query,
> +		 enum rte_flow_query_update_mode qu_mode,
> +		 struct rte_flow_error *error);
>  	/** See rte_flow_tunnel_decap_set() */
>  	int (*tunnel_decap_set)
>  		(struct rte_eth_dev *dev,
> @@ -276,6 +283,14 @@ struct rte_flow_ops {
>  		 void *data,
>  		 void *user_data,
>  		 struct rte_flow_error *error);
> +	/** See rte_flow_async_action_handle_query_update */
> +	int (*async_action_handle_query_update)
> +		(struct rte_eth_dev *dev, uint32_t queue_id,
> +		 const struct rte_flow_op_attr *op_attr,
> +		 struct rte_flow_action_handle *action_handle,
> +		 const void *update, void *query,
> +		 enum rte_flow_query_update_mode qu_mode,
> +		 void *user_data, struct rte_flow_error *error);
>  };
> 
>  /**
> diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
> index 17201fbe0f..42f0d7b30c 100644
> --- a/lib/ethdev/version.map
> +++ b/lib/ethdev/version.map
> @@ -298,6 +298,11 @@ EXPERIMENTAL {
>  	rte_flow_get_q_aged_flows;
>  	rte_mtr_meter_policy_get;
>  	rte_mtr_meter_profile_get;
> +
> +	# future
> +	rte_flow_action_handle_query_update;
> +	rte_flow_async_action_handle_query_update;
> +
>  };
> 
>  INTERNAL {
> --
> 2.34.1

Best,
Ori


^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH 2/2] ethdev: add quota flow action and item
  2022-12-21  7:35 ` [PATCH 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-01-08 13:47   ` Ori Kam
  0 siblings, 0 replies; 58+ messages in thread
From: Ori Kam @ 2023-01-08 13:47 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: Matan Azrad, Raslan Darawsheh, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Slava Ovsiienko,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Andrew Rybchenko

Hi Gregory,

> -----Original Message-----
> From: Gregory Etelson <getelson@nvidia.com>
> Sent: Wednesday, 21 December 2022 9:36
> 
> Quota action limits traffic according to pre-defined configuration.
> Quota reflects overall traffic usage regardless bandwidth.
> Quota flow action initialized with signed tokens number value.
> Quota flow action updates tokens number according to
> these rules:
> 1. if quota was configured to count packet length, for each packet
>    of size S, tokens number reduced by S.
> 2. If quota was configured to count packets, each packet decrements
>    tokens number.
> quota action sets packet metadata according to a number of remaining
> tokens number:
>   PASS - remaining tokens number is non-negative.
>   BLOCK - remaining tokens number is negative.
> 
> Quota flow item matches on that data
> 
> Application updates tokens number in quota flow action
> with SET or ADD calls:
>  SET(QUOTA, val) - arm quota with new tokens number set to val
>  ADD(QUOTA, val) - increase existing quota tokens number by val
> 
> Both SET and ADD return to application number of tokens stored in port
> before update.
> 
> Application must create a rule with quota action to mark flow and
> match on the mark with quota item in following flow rule.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> ---

Acked-by: Ori Kam <orika@nvidia.com>

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v2 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
  2022-12-21  7:35 ` [PATCH 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-01-04  9:56 ` [PATCH 1/2] ethdev: add query_update sync and async function calls Ori Kam
@ 2023-01-11  9:22 ` Gregory Etelson
  2023-01-11  9:22   ` [PATCH v2 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-01-11 12:20 ` [PATCH v3 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-11  9:22 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If port hardware allows both update and query in a single operation,
application still has to issue 2 separate hardware requests.

The patch adds `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

int
rte_flow_action_handle_query_update
       (uint16_t port_id, struct rte_flow_action_handle *handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        struct rte_flow_error *error);

int
rte_flow_async_action_handle_query_update
       (uint16_t port_id, uint32_t queue_id,
        const struct rte_flow_op_attr *op_attr,
        struct rte_flow_action_handle *action_handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        void *user_data, struct rte_flow_error *error);

Application can control query and update order, if that is supported
by port hardware, by setting qu_mode parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values provide
query only and update only functionality for backward compatibility
with existing API.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2: remove RTE_FLOW_QU_DEFAULT query-update mode
---
 lib/ethdev/rte_flow.c        |  39 +++++++++++++
 lib/ethdev/rte_flow.h        | 103 +++++++++++++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h |  15 +++++
 lib/ethdev/version.map       |   5 ++
 4 files changed, 162 insertions(+)

diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..8b8aa940be 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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 (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..f1ba163ac5 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,109 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query_update operational mode.
+ *
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before update.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
+	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If update parameter is NULL the function queries indirect action.
+ * If query parameter is NULL the function updates indirect action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update carried in order
+ * specified in the mode parameter.
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] handle
+ *   Handle for the indirect action object to be updated.
+ * @param update[in]
+ *   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] query
+ *   Pointer to storage for the associated query data type.
+ * @param[in] mode
+ *   Operational mode.
+ * @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_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param[in] port_id
+ *   Port identifier of Ethernet device.
+ * @param[in] queue_id
+ *   Flow queue which is used to update the rule.
+ * @param[in] attr
+ *   Indirect action update operation attributes.
+ * @param[in] 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[in] query
+ *   Pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param[in] mode
+ *   Operational mode.
+ * @param[in] user_data
+ *   The user data that will be returned on async completion event.
+ * @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_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..42f0d7b30c 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# future
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v2 2/2] ethdev: add quota flow action and item
  2023-01-11  9:22 ` [PATCH v2 " Gregory Etelson
@ 2023-01-11  9:22   ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-11  9:22 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Viacheslav Ovsiienko, Thomas Monjalon,
	Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: remove RTE_FLOW_QU_DEFAULT query-update mode
---
 app/test-pmd/cmdline_flow.c          | 394 ++++++++++++++++++++++++++-
 app/test-pmd/config.c                |  82 +++++-
 app/test-pmd/testpmd.h               |  11 +
 doc/guides/nics/features/default.ini |   2 +
 doc/guides/nics/features/mlx5.ini    |   2 +
 doc/guides/nics/mlx5.rst             |  12 +
 doc/guides/prog_guide/rte_flow.rst   |  41 +++
 lib/ethdev/rte_flow.c                |   2 +
 lib/ethdev/rte_flow.h                | 123 +++++++++
 9 files changed, 659 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/nics/features/mlx5.ini b/doc/guides/nics/features/mlx5.ini
index 62fd330e2b..ff9ea0cb43 100644
--- a/doc/guides/nics/features/mlx5.ini
+++ b/doc/guides/nics/features/mlx5.ini
@@ -80,6 +80,7 @@ mpls                 = Y
 nvgre                = Y
 port_id              = Y
 port_representor     = Y
+quota                = Y
 tag                  = Y
 tcp                  = Y
 udp                  = Y
@@ -112,6 +113,7 @@ of_set_vlan_pcp      = Y
 of_set_vlan_vid      = Y
 port_id              = Y
 queue                = Y
+quota                = Y
 raw_decap            = Y
 raw_encap            = Y
 represented_port     = Y
diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst
index 51f51259e3..1aac8becf2 100644
--- a/doc/guides/nics/mlx5.rst
+++ b/doc/guides/nics/mlx5.rst
@@ -291,6 +291,18 @@ Limitations
 - No Tx metadata go to the E-Switch steering domain for the Flow group 0.
   The flows within group 0 and set metadata action are rejected by hardware.
 
+- Quota:
+
+  - Template API only (HWS).
+  - Quota flow action and item supported in non-root HWS tables – flow group must be > 0.
+  - Quota implemented as indirect flow action only.
+  - Maximal value for quota SET and ADD operations in INT32_MAX (2G)
+  - Application cannot use 2 consecutive ADD updates.
+    Next tokens update after ADD must always be SET.
+  - HW can reduce non-negative quota to negative value.
+  - Quota flow action cannot be used with Meter or CT flow actions in the same rule.
+  - Maximal number of HW quota and HW meter objects <= 16e6.
+
 .. note::
 
    MAC addresses not already present in the bridge table of the associated
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 8b8aa940be..6439de3c1d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index f1ba163ac5..ed718057eb 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply quota verdict - PASS or BLOCK to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /** quota operational mode */
+	int64_t quota;                 /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /** update operation */
+	int64_t quota;                    /** quota value */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* RE: [PATCH 1/2] ethdev: add query_update sync and async function calls
  2023-01-04  9:56 ` [PATCH 1/2] ethdev: add query_update sync and async function calls Ori Kam
@ 2023-01-11  9:28   ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-11  9:28 UTC (permalink / raw)
  To: Ori Kam, dev
  Cc: Matan Azrad, Raslan Darawsheh,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit, Andrew Rybchenko

Hello Ori,
 
> Some comments below,

I posted v2 with updates.

Regards,
Gregory

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v3 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (2 preceding siblings ...)
  2023-01-11  9:22 ` [PATCH v2 " Gregory Etelson
@ 2023-01-11 12:20 ` Gregory Etelson
  2023-01-11 12:20   ` [PATCH v3 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-01-18 10:31 ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-11 12:20 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If port hardware allows both update and query in a single operation,
application still has to issue 2 separate hardware requests.

The patch adds `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

int
rte_flow_action_handle_query_update
       (uint16_t port_id, struct rte_flow_action_handle *handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        struct rte_flow_error *error);

int
rte_flow_async_action_handle_query_update
       (uint16_t port_id, uint32_t queue_id,
        const struct rte_flow_op_attr *op_attr,
        struct rte_flow_action_handle *action_handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        void *user_data, struct rte_flow_error *error);

Application can control query and update order, if that is supported
by port hardware, by setting qu_mode parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values provide
query only and update only functionality for backward compatibility
with existing API.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2: remove RTE_FLOW_QU_DEFAULT query-update mode
v3: update release release notes
    fix doxygen errors
---
 doc/guides/rel_notes/release_23_03.rst |   7 ++
 lib/ethdev/rte_flow.c                  |  39 ++++++++++
 lib/ethdev/rte_flow.h                  | 103 +++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  15 ++++
 lib/ethdev/version.map                 |   5 ++
 5 files changed, 169 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index b8c5b68d6c..0f13c68578 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -55,6 +55,13 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..8b8aa940be 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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 (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..2447d58bfd 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,109 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query_update operational mode.
+ *
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before update.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
+	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If update parameter is NULL the function queries indirect action.
+ * If query parameter is NULL the function updates indirect action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update carried in order
+ * specified in the mode parameter.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param 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 query
+ *   Pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ * @param 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_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param 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 query
+ *   Pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..42f0d7b30c 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# future
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v3 2/2] ethdev: add quota flow action and item
  2023-01-11 12:20 ` [PATCH v3 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
@ 2023-01-11 12:20   ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-11 12:20 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Viacheslav Ovsiienko, Thomas Monjalon,
	Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: remove RTE_FLOW_QU_DEFAULT query-update mode
v3: update release notes
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/nics/features/mlx5.ini      |   2 +
 doc/guides/nics/mlx5.rst               |  12 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |  27 ++
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 10 files changed, 686 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/nics/features/mlx5.ini b/doc/guides/nics/features/mlx5.ini
index 62fd330e2b..ff9ea0cb43 100644
--- a/doc/guides/nics/features/mlx5.ini
+++ b/doc/guides/nics/features/mlx5.ini
@@ -80,6 +80,7 @@ mpls                 = Y
 nvgre                = Y
 port_id              = Y
 port_representor     = Y
+quota                = Y
 tag                  = Y
 tcp                  = Y
 udp                  = Y
@@ -112,6 +113,7 @@ of_set_vlan_pcp      = Y
 of_set_vlan_vid      = Y
 port_id              = Y
 queue                = Y
+quota                = Y
 raw_decap            = Y
 raw_encap            = Y
 represented_port     = Y
diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst
index 51f51259e3..1aac8becf2 100644
--- a/doc/guides/nics/mlx5.rst
+++ b/doc/guides/nics/mlx5.rst
@@ -291,6 +291,18 @@ Limitations
 - No Tx metadata go to the E-Switch steering domain for the Flow group 0.
   The flows within group 0 and set metadata action are rejected by hardware.
 
+- Quota:
+
+  - Template API only (HWS).
+  - Quota flow action and item supported in non-root HWS tables – flow group must be > 0.
+  - Quota implemented as indirect flow action only.
+  - Maximal value for quota SET and ADD operations in INT32_MAX (2G)
+  - Application cannot use 2 consecutive ADD updates.
+    Next tokens update after ADD must always be SET.
+  - HW can reduce non-negative quota to negative value.
+  - Quota flow action cannot be used with Meter or CT flow actions in the same rule.
+  - Maximal number of HW quota and HW meter objects <= 16e6.
+
 .. note::
 
    MAC addresses not already present in the bridge table of the associated
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 0f13c68578..def395ef4c 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -63,6 +63,33 @@ New Features
   - ``rte_flow_action_handle_query_update``
   - ``rte_flow_async_action_handle_query_update``
 
+* **Added quota flow action and quota flow item.**
+
+  Quota action limits traffic according to pre-defined configuration.
+  Quota reflects overall traffic usage regardless bandwidth.
+  Quota flow action initialized with signed tokens number value.
+  Quota flow action updates tokens number according to
+  these rules:
+  * If quota was configured to count packet length, for each packet
+  of size S, tokens number reduced by S.
+  * If quota was configured to count packets, each packet decrements
+  tokens number.
+  Quota action sets packet metadata according to a number of remaining
+  tokens number:
+  * ``PASS`` - remaining tokens number is non-negative.
+  * ``BLOCK`` - remaining tokens number is negative.
+
+  Quota flow item matches on that metadata.
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added tokens to ``token_list[]`` for flow quota action and item support.
+
+
+
 Removed Items
 -------------
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 8b8aa940be..6439de3c1d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 2447d58bfd..e5309e27c8 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply quota verdict - PASS or BLOCK to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /** quota operational mode */
+	int64_t quota;                 /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /** update operation */
+	int64_t quota;                    /** quota value */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v4 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (3 preceding siblings ...)
  2023-01-11 12:20 ` [PATCH v3 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
@ 2023-01-18 10:31 ` Gregory Etelson
  2023-01-18 10:31   ` [PATCH v4 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-01-18 13:56   ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Thomas Monjalon
  2023-01-19 13:25 ` [PATCH v5 " Gregory Etelson
                   ` (5 subsequent siblings)
  10 siblings, 2 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-18 10:31 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If port hardware allows both update and query in a single operation,
application still has to issue 2 separate hardware requests.

The patch adds `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

int
rte_flow_action_handle_query_update
       (uint16_t port_id, struct rte_flow_action_handle *handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        struct rte_flow_error *error);

int
rte_flow_async_action_handle_query_update
       (uint16_t port_id, uint32_t queue_id,
        const struct rte_flow_op_attr *op_attr,
        struct rte_flow_action_handle *action_handle,
        const void *update, void *query,
        enum rte_flow_query_update_mode mode,
        void *user_data, struct rte_flow_error *error);

Application can control query and update order, if that is supported
by port hardware, by setting qu_mode parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values provide
query only and update only functionality for backward compatibility
with existing API.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release release notes.
    Fix doxygen errors.
v4: Add returned errno codes. 
---
 doc/guides/rel_notes/release_23_03.rst |   7 ++
 lib/ethdev/rte_flow.c                  |  39 +++++++++
 lib/ethdev/rte_flow.h                  | 107 +++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  15 ++++
 lib/ethdev/version.map                 |   5 ++
 5 files changed, 173 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index b8c5b68d6c..0f13c68578 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -55,6 +55,13 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..8b8aa940be 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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 (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..817a2fabef 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,113 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query_update operational mode.
+ *
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before update.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
+	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If update parameter is NULL the function queries indirect action.
+ * If query parameter is NULL the function updates indirect action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update carried in order
+ * specified in the mode parameter.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param 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 query
+ *   Pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ * @param 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.
+ * - (ENOTSUP) if underlying device does not support this functionality.
+ * - (EINVAL) - if *handle* invalid
+ */
+__rte_experimental
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param 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 query
+ *   Pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ * - (ENOTSUP) if underlying device does not support this functionality.
+ * - (EINVAL) - if *handle* invalid
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..42f0d7b30c 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# future
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v4 2/2] ethdev: add quota flow action and item
  2023-01-18 10:31 ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
@ 2023-01-18 10:31   ` Gregory Etelson
  2023-01-18 14:03     ` Thomas Monjalon
  2023-01-18 13:56   ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Thomas Monjalon
  1 sibling, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-18 10:31 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Viacheslav Ovsiienko, Thomas Monjalon,
	Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release notes.
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/nics/features/mlx5.ini      |   2 +
 doc/guides/nics/mlx5.rst               |  12 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |  27 ++
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 10 files changed, 686 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/nics/features/mlx5.ini b/doc/guides/nics/features/mlx5.ini
index 62fd330e2b..ff9ea0cb43 100644
--- a/doc/guides/nics/features/mlx5.ini
+++ b/doc/guides/nics/features/mlx5.ini
@@ -80,6 +80,7 @@ mpls                 = Y
 nvgre                = Y
 port_id              = Y
 port_representor     = Y
+quota                = Y
 tag                  = Y
 tcp                  = Y
 udp                  = Y
@@ -112,6 +113,7 @@ of_set_vlan_pcp      = Y
 of_set_vlan_vid      = Y
 port_id              = Y
 queue                = Y
+quota                = Y
 raw_decap            = Y
 raw_encap            = Y
 represented_port     = Y
diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst
index 51f51259e3..1aac8becf2 100644
--- a/doc/guides/nics/mlx5.rst
+++ b/doc/guides/nics/mlx5.rst
@@ -291,6 +291,18 @@ Limitations
 - No Tx metadata go to the E-Switch steering domain for the Flow group 0.
   The flows within group 0 and set metadata action are rejected by hardware.
 
+- Quota:
+
+  - Template API only (HWS).
+  - Quota flow action and item supported in non-root HWS tables – flow group must be > 0.
+  - Quota implemented as indirect flow action only.
+  - Maximal value for quota SET and ADD operations in INT32_MAX (2G)
+  - Application cannot use 2 consecutive ADD updates.
+    Next tokens update after ADD must always be SET.
+  - HW can reduce non-negative quota to negative value.
+  - Quota flow action cannot be used with Meter or CT flow actions in the same rule.
+  - Maximal number of HW quota and HW meter objects <= 16e6.
+
 .. note::
 
    MAC addresses not already present in the bridge table of the associated
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 0f13c68578..def395ef4c 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -63,6 +63,33 @@ New Features
   - ``rte_flow_action_handle_query_update``
   - ``rte_flow_async_action_handle_query_update``
 
+* **Added quota flow action and quota flow item.**
+
+  Quota action limits traffic according to pre-defined configuration.
+  Quota reflects overall traffic usage regardless bandwidth.
+  Quota flow action initialized with signed tokens number value.
+  Quota flow action updates tokens number according to
+  these rules:
+  * If quota was configured to count packet length, for each packet
+  of size S, tokens number reduced by S.
+  * If quota was configured to count packets, each packet decrements
+  tokens number.
+  Quota action sets packet metadata according to a number of remaining
+  tokens number:
+  * ``PASS`` - remaining tokens number is non-negative.
+  * ``BLOCK`` - remaining tokens number is negative.
+
+  Quota flow item matches on that metadata.
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added tokens to ``token_list[]`` for flow quota action and item support.
+
+
+
 Removed Items
 -------------
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 8b8aa940be..6439de3c1d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 817a2fabef..c51969fe46 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply quota verdict - PASS or BLOCK to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /** quota operational mode */
+	int64_t quota;                 /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /** update operation */
+	int64_t quota;                    /** quota value */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH v4 1/2] ethdev: add query_update sync and async function calls
  2023-01-18 10:31 ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
  2023-01-18 10:31   ` [PATCH v4 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-01-18 13:56   ` Thomas Monjalon
  2023-01-18 17:34     ` Gregory Etelson
  1 sibling, 1 reply; 58+ messages in thread
From: Thomas Monjalon @ 2023-01-18 13:56 UTC (permalink / raw)
  To: Gregory Etelson
  Cc: dev, matan, rasland, Ori Kam, Ferruh Yigit, Andrew Rybchenko, ivan.malov

18/01/2023 11:31, Gregory Etelson:
> Current API allows either query or update indirect flow action.
> If port hardware allows both update and query in a single operation,
> application still has to issue 2 separate hardware requests.
> 
> The patch adds `rte_flow_action_handle_query_update` function call,
> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.

What is the benefit?
Is it a performance optimization? How much?

> int
> rte_flow_action_handle_query_update
>        (uint16_t port_id, struct rte_flow_action_handle *handle,
>         const void *update, void *query,
>         enum rte_flow_query_update_mode mode,
>         struct rte_flow_error *error);
> 
> int
> rte_flow_async_action_handle_query_update
>        (uint16_t port_id, uint32_t queue_id,
>         const struct rte_flow_op_attr *op_attr,
>         struct rte_flow_action_handle *action_handle,
>         const void *update, void *query,
>         enum rte_flow_query_update_mode mode,
>         void *user_data, struct rte_flow_error *error);

No need to copy the functions in the commit log.

> Application can control query and update order, if that is supported
> by port hardware, by setting qu_mode parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values provide
> query only and update only functionality for backward compatibility
> with existing API.

Why do we need such compatibility?
The existing functions will stay, isn't it?

[...]
> + * RTE_FLOW_QU_QUERY_FIRST
> + *   Force port to query action before update.
> + * RTE_FLOW_QU_UPDATE_FIRST
> + *   Force port to update action before update.

You mean "before query".
Anyway you don't need to repeat the enum in the comment.

> + *
> + * @see rte_flow_action_handle_query_update()
> + * @see rte_flow_async_action_handle_query_update()
> + */
> +enum rte_flow_query_update_mode {
> +	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
> +	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and/or update indirect flow action.
> + * If update parameter is NULL the function queries indirect action.
> + * If query parameter is NULL the function updates indirect action.
> + * If both query and update not NULL, the function atomically
> + * queries and updates indirect action. Query and update carried in order

"are" carried?

> + * specified in the mode parameter.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param update
> + *   Update profile specification used to modify the action pointed by handle.
> + *   *update* could be with the same type of the immediate action corresponding

could be "of" the same type

> + *   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 query
> + *   Pointer to storage for the associated query data type.
> + * @param mode
> + *   Operational mode.
> + * @param 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.
> + * - (ENOTSUP) if underlying device does not support this functionality.
> + * - (EINVAL) - if *handle* invalid
> + */
> +__rte_experimental
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode mode,
> +				    struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue async indirect flow action query and/or update
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue which is used to update the rule.
> + * @param attr
> + *   Indirect action update operation attributes.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param 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

includes -> including

> + *   action configuration to be updated and bit fields to indicate the member
> + *   of fields inside the action to update.

Where can we find how such wrapper should look like?

> + * @param query
> + *   Pointer to storage for the associated query data type.
> + *   Query result returned on async completion event.
> + * @param mode
> + *   Operational mode.
> + * @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 on success, a negative errno value otherwise and rte_errno is set.
> + * - (ENOTSUP) if underlying device does not support this functionality.
> + * - (EINVAL) - if *handle* invalid
> + */
> +__rte_experimental
> +int
> +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle *handle,
> +					  const void *update, void *query,
> +					  enum rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  struct rte_flow_error *error);

> --- a/lib/ethdev/version.map
> +++ b/lib/ethdev/version.map
> @@ -298,6 +298,11 @@ EXPERIMENTAL {
>  	rte_flow_get_q_aged_flows;
>  	rte_mtr_meter_policy_get;
>  	rte_mtr_meter_profile_get;
> +
> +	# future

It should be "# added in 23.03"

> +	rte_flow_action_handle_query_update;
> +	rte_flow_async_action_handle_query_update;




^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v4 2/2] ethdev: add quota flow action and item
  2023-01-18 10:31   ` [PATCH v4 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-01-18 14:03     ` Thomas Monjalon
  2023-01-19  9:13       ` Gregory Etelson
  0 siblings, 1 reply; 58+ messages in thread
From: Thomas Monjalon @ 2023-01-18 14:03 UTC (permalink / raw)
  To: Gregory Etelson
  Cc: dev, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Viacheslav Ovsiienko, Andrew Rybchenko, ivan.malov,
	jerinj

Is this patch related to query/update of indirect flow action?
It looks like it should be in a separate series.

18/01/2023 11:31, Gregory Etelson:
> Quota action limits traffic according to pre-defined configuration.
> Quota reflects overall traffic usage regardless bandwidth.
> Quota flow action initialized with signed tokens number value.
> Quota flow action updates tokens number according to
> these rules:
> 1. if quota was configured to count packet length, for each packet
>    of size S, tokens number reduced by S.
> 2. If quota was configured to count packets, each packet decrements
>    tokens number.
> quota action sets packet metadata according to a number of remaining
> tokens number:
>   PASS - remaining tokens number is non-negative.
>   BLOCK - remaining tokens number is negative.
> 
> Quota flow item matches on that data
> 
> Application updates tokens number in quota flow action
> with SET or ADD calls:
>  SET(QUOTA, val) - arm quota with new tokens number set to val
>  ADD(QUOTA, val) - increase existing quota tokens number by val
> 
> Both SET and ADD return to application number of tokens stored in port
> before update.
> 
> Application must create a rule with quota action to mark flow and
> match on the mark with quota item in following flow rule.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>
> ---
> v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> v3: Update release notes.
> ---
>  app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
>  app/test-pmd/config.c                  |  82 ++++-
>  app/test-pmd/testpmd.h                 |  11 +
>  doc/guides/nics/features/default.ini   |   2 +
>  doc/guides/nics/features/mlx5.ini      |   2 +
>  doc/guides/nics/mlx5.rst               |  12 +

mlx5 feature should be documented and marked in the patch
having the implementation code, not in ethdev API.

>  doc/guides/prog_guide/rte_flow.rst     |  41 +++
>  doc/guides/rel_notes/release_23_03.rst |  27 ++
>  lib/ethdev/rte_flow.c                  |   2 +
>  lib/ethdev/rte_flow.h                  | 123 ++++++++
>  10 files changed, 686 insertions(+), 10 deletions(-)
[...]
> +	/**
> +	 * Apply quota verdict - PASS or BLOCK to a flow.

Is "to" really wanted here?
What about "pass or block a flow"?

> +	 *
> +	 * @see struct rte_flow_action_quota
> +	 * @see struct rte_flow_query_quota
> +	 * @see struct rte_flow_update_quota
> +	 */
> +	 RTE_FLOW_ACTION_TYPE_QUOTA,
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * QUOTA operational mode.
> + *
> + * @see struct rte_flow_action_quota
> + */
> +enum rte_flow_quota_mode {
> +	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
> +	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
> +	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Create QUOTA action.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + */
> +struct rte_flow_action_quota {
> +	enum rte_flow_quota_mode mode; /** quota operational mode */
> +	int64_t quota;                 /** quota value */

What means a negative quota?

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query indirect QUOTA action.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + *
> + */
> +struct rte_flow_query_quota {
> +	int64_t quota; /** quota value */
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Indirect QUOTA update operations.
> + *
> + * @see struct rte_flow_update_quota
> + */
> +enum rte_flow_update_quota_op {
> +	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
> +	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + *
> + * Update indirect QUOTA action.
> + */
> +struct rte_flow_update_quota {
> +	enum rte_flow_update_quota_op op; /** update operation */
> +	int64_t quota;                    /** quota value */
>  };
>  
>  /**
> @@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
>  	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
>  	 */
>  	uint32_t max_nb_conn_tracks;
> +	/**
> +	 * Maximum number of quota actions.
> +	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
> +	 */
> +	uint32_t max_nb_quotas;
>  	/**
>  	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
>  	 */
> @@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
>  	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
>  	 */
>  	uint32_t nb_conn_tracks;
> +	/**
> +	 * Maximum number of quota actions.
> +	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
> +	 */
> +	uint32_t nb_quotas;
>  	/**
>  	 * Port flags (RTE_FLOW_PORT_FLAG_*).
>  	 */




^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v4 1/2] ethdev: add query_update sync and async function calls
  2023-01-18 13:56   ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Thomas Monjalon
@ 2023-01-18 17:34     ` Gregory Etelson
  2023-01-19  8:44       ` Thomas Monjalon
  0 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-18 17:34 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon (EXTERNAL)
  Cc: dev, Matan Azrad, Raslan Darawsheh, Ori Kam, Ferruh Yigit,
	Andrew Rybchenko, ivan.malov

Hello Thomas,

[]

> > Current API allows either query or update indirect flow action.
> > If port hardware allows both update and query in a single operation,
> > application still has to issue 2 separate hardware requests.
> >
> > The patch adds `rte_flow_action_handle_query_update` function call,
> > and it's async version `rte_flow_async_action_handle_query_update`
> > to atomically query and update flow action.
> 
> What is the benefit?
> Is it a performance optimization? How much?
>

rte_flow_action_handle_query_update() can query data and conditionally update it in a single HW operation.
That provides accuracy that cannot be achieved with existing API.
For example, quota flow action must be updated when it in the PASS state only. 
If application use existing query, by the time it gets query data and analyzes it, HW object state can change.
As the result application update action will not reflect HW configuration.

The function can provide general optimization, but that was not tested.
 
> > int
> > rte_flow_action_handle_query_update
> >        (uint16_t port_id, struct rte_flow_action_handle *handle,
> >         const void *update, void *query,
> >         enum rte_flow_query_update_mode mode,
> >         struct rte_flow_error *error);
> >
> > int
> > rte_flow_async_action_handle_query_update
> >        (uint16_t port_id, uint32_t queue_id,
> >         const struct rte_flow_op_attr *op_attr,
> >         struct rte_flow_action_handle *action_handle,
> >         const void *update, void *query,
> >         enum rte_flow_query_update_mode mode,
> >         void *user_data, struct rte_flow_error *error);
> 
> No need to copy the functions in the commit log.
> 

Will be updated in v5

> > Application can control query and update order, if that is supported
> > by port hardware, by setting qu_mode parameter to
> > RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> >
> > RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values
> provide
> > query only and update only functionality for backward compatibility
> > with existing API.
> 
> Why do we need such compatibility?
> The existing functions will stay, isn't it?
> 

rte_flow_action_handle_query_update() has extended functionality with better HW support.
The plan is to deprecate and replace existing query and update functions.

> [...]
> > + * RTE_FLOW_QU_QUERY_FIRST
> > + *   Force port to query action before update.
> > + * RTE_FLOW_QU_UPDATE_FIRST
> > + *   Force port to update action before update.
> 
> You mean "before query".
> Anyway you don't need to repeat the enum in the comment.
> 

Correct, must be "before query".
Will fix in v5.

> > + *
> > + * @see rte_flow_action_handle_query_update()
> > + * @see rte_flow_async_action_handle_query_update()
> > + */
> > +enum rte_flow_query_update_mode {
> > +     RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
> > +     RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query and/or update indirect flow action.
> > + * If update parameter is NULL the function queries indirect action.
> > + * If query parameter is NULL the function updates indirect action.
> > + * If both query and update not NULL, the function atomically
> > + * queries and updates indirect action. Query and update carried in
> order
> 
> "are" carried?
> 

Typo. Will fix in v5.

> > + * specified in the mode parameter.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param update
> > + *   Update profile specification used to modify the action pointed by
> handle.
> > + *   *update* could be with the same type of the immediate action
> corresponding
> 
> could be "of" the same type
> 

Will fix in v5.

> > + *   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 query
> > + *   Pointer to storage for the associated query data type.
> > + * @param mode
> > + *   Operational mode.
> > + * @param 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.
> > + * - (ENOTSUP) if underlying device does not support this functionality.
> > + * - (EINVAL) - if *handle* invalid
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_action_handle_query_update(uint16_t port_id,
> > +                                 struct rte_flow_action_handle *handle,
> > +                                 const void *update, void *query,
> > +                                 enum rte_flow_query_update_mode mode,
> > +                                 struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue async indirect flow action query and/or update
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue which is used to update the rule.
> > + * @param attr
> > + *   Indirect action update operation attributes.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param 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
> 
> includes -> including
> 

Will fix in v5.

> > + *   action configuration to be updated and bit fields to indicate the
> member
> > + *   of fields inside the action to update.
> 
> Where can we find how such wrapper should look like?
>

That patch will be sent later.
I'll rephrase.
 
> > + * @param query
> > + *   Pointer to storage for the associated query data type.
> > + *   Query result returned on async completion event.
> > + * @param mode
> > + *   Operational mode.
> > + * @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 on success, a negative errno value otherwise and rte_errno is set.
> > + * - (ENOTSUP) if underlying device does not support this functionality.
> > + * - (EINVAL) - if *handle* invalid
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> > +                                       const struct rte_flow_op_attr *attr,
> > +                                       struct rte_flow_action_handle *handle,
> > +                                       const void *update, void *query,
> > +                                       enum rte_flow_query_update_mode mode,
> > +                                       void *user_data,
> > +                                       struct rte_flow_error *error);
> 
> > --- a/lib/ethdev/version.map
> > +++ b/lib/ethdev/version.map
> > @@ -298,6 +298,11 @@ EXPERIMENTAL {
> >       rte_flow_get_q_aged_flows;
> >       rte_mtr_meter_policy_get;
> >       rte_mtr_meter_profile_get;
> > +
> > +     # future
> 
> It should be "# added in 23.03"
> 

Will fix in v5.

> > +     rte_flow_action_handle_query_update;
> > +     rte_flow_async_action_handle_query_update;
> 
> 


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v4 1/2] ethdev: add query_update sync and async function calls
  2023-01-18 17:34     ` Gregory Etelson
@ 2023-01-19  8:44       ` Thomas Monjalon
  0 siblings, 0 replies; 58+ messages in thread
From: Thomas Monjalon @ 2023-01-19  8:44 UTC (permalink / raw)
  To: Gregory Etelson
  Cc: dev, Matan Azrad, Raslan Darawsheh, Ori Kam, Ferruh Yigit,
	Andrew Rybchenko, ivan.malov

18/01/2023 18:34, Gregory Etelson:
> > > Current API allows either query or update indirect flow action.
> > > If port hardware allows both update and query in a single operation,
> > > application still has to issue 2 separate hardware requests.
> > >
> > > The patch adds `rte_flow_action_handle_query_update` function call,
> > > and it's async version `rte_flow_async_action_handle_query_update`
> > > to atomically query and update flow action.
> > 
> > What is the benefit?
> > Is it a performance optimization? How much?
> >
> 
> rte_flow_action_handle_query_update() can query data and conditionally update it in a single HW operation.
> That provides accuracy that cannot be achieved with existing API.
> For example, quota flow action must be updated when it in the PASS state only. 
> If application use existing query, by the time it gets query data and analyzes it, HW object state can change.
> As the result application update action will not reflect HW configuration.

An explanation of the atomicity benefit should be in the commit log.

> The function can provide general optimization, but that was not tested.


> > > Application can control query and update order, if that is supported
> > > by port hardware, by setting qu_mode parameter to
> > > RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> > >
> > > RTE_FLOW_QU_QUERY and RTE_FLOW_QU_UPDATE parameter values
> > provide
> > > query only and update only functionality for backward compatibility
> > > with existing API.
> > 
> > Why do we need such compatibility?
> > The existing functions will stay, isn't it?
> > 
> 
> rte_flow_action_handle_query_update() has extended functionality with better HW support.

You cannot argue HW support without knowing what would be the support
of other vendors.

> The plan is to deprecate and replace existing query and update functions.

I disagree with such deprecation.
Anyway it is not the topic of this patch, so I suggest to remove the sentence
about compatibility.



^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v4 2/2] ethdev: add quota flow action and item
  2023-01-18 14:03     ` Thomas Monjalon
@ 2023-01-19  9:13       ` Gregory Etelson
  2023-01-19  9:31         ` Thomas Monjalon
  0 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-19  9:13 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon (EXTERNAL)
  Cc: dev, Matan Azrad, Raslan Darawsheh, Ori Kam, Aman Singh,
	Yuying Zhang, Ferruh Yigit, Slava Ovsiienko, Andrew Rybchenko,
	ivan.malov, jerinj

Hello Thomas,

> Is this patch related to query/update of indirect flow action?
> It looks like it should be in a separate series.
> 

Quota flow action updates relay on rte_flow_action_handle_query_update().
Also, the function is used in the testmd patch section.

> 18/01/2023 11:31, Gregory Etelson:
> > Quota action limits traffic according to pre-defined configuration.
> > Quota reflects overall traffic usage regardless bandwidth.
> > Quota flow action initialized with signed tokens number value.
> > Quota flow action updates tokens number according to
> > these rules:
> > 1. if quota was configured to count packet length, for each packet
> >    of size S, tokens number reduced by S.
> > 2. If quota was configured to count packets, each packet decrements
> >    tokens number.
> > quota action sets packet metadata according to a number of remaining
> > tokens number:
> >   PASS - remaining tokens number is non-negative.
> >   BLOCK - remaining tokens number is negative.
> >
> > Quota flow item matches on that data
> >
> > Application updates tokens number in quota flow action
> > with SET or ADD calls:
> >  SET(QUOTA, val) - arm quota with new tokens number set to val
> >  ADD(QUOTA, val) - increase existing quota tokens number by val
> >
> > Both SET and ADD return to application number of tokens stored in port
> > before update.
> >
> > Application must create a rule with quota action to mark flow and
> > match on the mark with quota item in following flow rule.
> >
> > Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> > Acked-by: Ori Kam <orika@nvidia.com>
> > ---
> > v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> > v3: Update release notes.
> > ---
> >  app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
> >  app/test-pmd/config.c                  |  82 ++++-
> >  app/test-pmd/testpmd.h                 |  11 +
> >  doc/guides/nics/features/default.ini   |   2 +
> >  doc/guides/nics/features/mlx5.ini      |   2 +
> >  doc/guides/nics/mlx5.rst               |  12 +
> 
> mlx5 feature should be documented and marked in the patch
> having the implementation code, not in ethdev API.
>

Will fix in v5.
 
> >  doc/guides/prog_guide/rte_flow.rst     |  41 +++
> >  doc/guides/rel_notes/release_23_03.rst |  27 ++
> >  lib/ethdev/rte_flow.c                  |   2 +
> >  lib/ethdev/rte_flow.h                  | 123 ++++++++
> >  10 files changed, 686 insertions(+), 10 deletions(-)
> [...]
> > +     /**
> > +      * Apply quota verdict - PASS or BLOCK to a flow.
> 
> Is "to" really wanted here?
> What about "pass or block a flow"?
> 

The flow action provides information only - it does not affect flow in any way.
Application needs to match on quota flow item after quota action to discover quota state.
It's also up to application how to react on quota state. 

> > +      *
> > +      * @see struct rte_flow_action_quota
> > +      * @see struct rte_flow_query_quota
> > +      * @see struct rte_flow_update_quota
> > +      */
> > +      RTE_FLOW_ACTION_TYPE_QUOTA,
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * QUOTA operational mode.
> > + *
> > + * @see struct rte_flow_action_quota
> > + */
> > +enum rte_flow_quota_mode {
> > +     RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
> > +     RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting
> from L2 */
> > +     RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting
> from L3 */
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Create QUOTA action.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + */
> > +struct rte_flow_action_quota {
> > +     enum rte_flow_quota_mode mode; /** quota operational mode */
> > +     int64_t quota;                 /** quota value */
> 
> What means a negative quota?
> 

Negative quota is kind of a loan extended by a hardware if it provides such option.
If quota value is -T, application must provide at least (T+1) tokens to return quota to PASS state. 

> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query indirect QUOTA action.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + *
> > + */
> > +struct rte_flow_query_quota {
> > +     int64_t quota; /** quota value */
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Indirect QUOTA update operations.
> > + *
> > + * @see struct rte_flow_update_quota
> > + */
> > +enum rte_flow_update_quota_op {
> > +     RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
> > +     RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with
> new value */
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + *
> > + * Update indirect QUOTA action.
> > + */
> > +struct rte_flow_update_quota {
> > +     enum rte_flow_update_quota_op op; /** update operation */
> > +     int64_t quota;                    /** quota value */
> >  };
> >
> >  /**
> > @@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
> >        * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
> >        */
> >       uint32_t max_nb_conn_tracks;
> > +     /**
> > +      * Maximum number of quota actions.
> > +      * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > +      */
> > +     uint32_t max_nb_quotas;
> >       /**
> >        * Port supported flags (RTE_FLOW_PORT_FLAG_*).
> >        */
> > @@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
> >        * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
> >        */
> >       uint32_t nb_conn_tracks;
> > +     /**
> > +      * Maximum number of quota actions.
> > +      * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > +      */
> > +     uint32_t nb_quotas;
> >       /**
> >        * Port flags (RTE_FLOW_PORT_FLAG_*).
> >        */
> 
> 


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v4 2/2] ethdev: add quota flow action and item
  2023-01-19  9:13       ` Gregory Etelson
@ 2023-01-19  9:31         ` Thomas Monjalon
  2023-01-19  9:39           ` Gregory Etelson
  0 siblings, 1 reply; 58+ messages in thread
From: Thomas Monjalon @ 2023-01-19  9:31 UTC (permalink / raw)
  To: Gregory Etelson
  Cc: dev, Matan Azrad, Raslan Darawsheh, Ori Kam, Aman Singh,
	Yuying Zhang, Ferruh Yigit, Slava Ovsiienko, Andrew Rybchenko,
	ivan.malov, jerinj

19/01/2023 10:13, Gregory Etelson:
> Hello Thomas,
> 
> > Is this patch related to query/update of indirect flow action?
> > It looks like it should be in a separate series.
> > 
> 
> Quota flow action updates relay on rte_flow_action_handle_query_update().
> Also, the function is used in the testmd patch section.

Yes we still don't have the tool in CI to mark series dependency.


> > > +     /**
> > > +      * Apply quota verdict - PASS or BLOCK to a flow.
> > 
> > Is "to" really wanted here?
> > What about "pass or block a flow"?
> > 
> 
> The flow action provides information only - it does not affect flow in any way.
> Application needs to match on quota flow item after quota action to discover quota state.
> It's also up to application how to react on quota state. 

I am still not sure to understand the comment.
Is this one better?
"Apply the quota verdict (PASS or BLOCK) to a flow."


> > > +struct rte_flow_action_quota {
> > > +     enum rte_flow_quota_mode mode; /** quota operational mode */
> > > +     int64_t quota;                 /** quota value */
> > 
> > What means a negative quota?
> 
> Negative quota is kind of a loan extended by a hardware if it provides such option.
> If quota value is -T, application must provide at least (T+1) tokens to return quota to PASS state. 

OK. Is it documented?




^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v4 2/2] ethdev: add quota flow action and item
  2023-01-19  9:31         ` Thomas Monjalon
@ 2023-01-19  9:39           ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-19  9:39 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon (EXTERNAL)
  Cc: dev, Matan Azrad, Raslan Darawsheh, Ori Kam, Aman Singh,
	Yuying Zhang, Ferruh Yigit, Slava Ovsiienko, Andrew Rybchenko,
	ivan.malov, jerinj

Hello Thomas,

[]

> > > > +     /**
> > > > +      * Apply quota verdict - PASS or BLOCK to a flow.
> > >
> > > Is "to" really wanted here?
> > > What about "pass or block a flow"?
> > >
> >
> > The flow action provides information only - it does not affect flow in any
> way.
> > Application needs to match on quota flow item after quota action to
> discover quota state.
> > It's also up to application how to react on quota state.
> 
> I am still not sure to understand the comment.
> Is this one better?
> "Apply the quota verdict (PASS or BLOCK) to a flow."
> 
> 

Yes. I'll update the v5.

> > > > +struct rte_flow_action_quota {
> > > > +     enum rte_flow_quota_mode mode; /** quota operational mode
> */
> > > > +     int64_t quota;                 /** quota value */
> > >
> > > What means a negative quota?
> >
> > Negative quota is kind of a loan extended by a hardware if it provides
> such option.
> > If quota value is -T, application must provide at least (T+1) tokens to
> return quota to PASS state.
> 
> OK. Is it documented?
> 
> 

I'll add missing documentation.


^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v5 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (4 preceding siblings ...)
  2023-01-18 10:31 ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
@ 2023-01-19 13:25 ` Gregory Etelson
  2023-01-19 13:25   ` [PATCH v5 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-01-19 16:47 ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-19 13:25 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If indirect action must be conditionally updated according to it's
present state application must first issue action query then
analyze returned data and if needed issue update request.
When the update will be processed, action state can change and
the update can invalidate the action.

The patch adds `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

Application can control query and update order, if that is supported
by port hardware, by setting `qu_mode` parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release release notes.
    Fix doxygen errors.
v4: Add returned errno codes.
v5: Update the patch description.
    Fix typos.
---
 doc/guides/rel_notes/release_23_03.rst |  7 ++
 lib/ethdev/rte_flow.c                  | 39 ++++++++++
 lib/ethdev/rte_flow.h                  | 99 ++++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           | 15 ++++
 lib/ethdev/version.map                 |  5 ++
 5 files changed, 165 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index b8c5b68d6c..0f13c68578 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -55,6 +55,13 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..8b8aa940be 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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 (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..280567a3ae 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,105 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query_update operational mode.
+ *
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before query.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
+	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If update parameter is NULL the function queries indirect action.
+ * If query parameter is NULL the function updates indirect action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update are carried in order
+ * specified in the mode parameter.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   Update profile specification used to modify the action pointed by handle.
+ * @param query
+ *   Pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ * @param 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.
+ * - (ENOTSUP) if underlying device does not support this functionality.
+ * - (EINVAL) - if *handle* invalid
+ */
+__rte_experimental
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   Update profile specification used to modify the action pointed by handle.
+ * @param query
+ *   Pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ * - (ENOTSUP) if underlying device does not support this functionality.
+ * - (EINVAL) - if *handle* invalid
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..628c486081 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# added in 23.03
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v5 2/2] ethdev: add quota flow action and item
  2023-01-19 13:25 ` [PATCH v5 " Gregory Etelson
@ 2023-01-19 13:25   ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-19 13:25 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Thomas Monjalon, Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

If quota state was BLOCK (negative action tokens number)
application can change it to PASS after providing enough tokens to
raise action tokens number to 0 or above.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release notes.
v5: Update the patch description.
    Remove PMD documentation.
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |  27 ++
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 8 files changed, 672 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 0f13c68578..def395ef4c 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -63,6 +63,33 @@ New Features
   - ``rte_flow_action_handle_query_update``
   - ``rte_flow_async_action_handle_query_update``
 
+* **Added quota flow action and quota flow item.**
+
+  Quota action limits traffic according to pre-defined configuration.
+  Quota reflects overall traffic usage regardless bandwidth.
+  Quota flow action initialized with signed tokens number value.
+  Quota flow action updates tokens number according to
+  these rules:
+  * If quota was configured to count packet length, for each packet
+  of size S, tokens number reduced by S.
+  * If quota was configured to count packets, each packet decrements
+  tokens number.
+  Quota action sets packet metadata according to a number of remaining
+  tokens number:
+  * ``PASS`` - remaining tokens number is non-negative.
+  * ``BLOCK`` - remaining tokens number is negative.
+
+  Quota flow item matches on that metadata.
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added tokens to ``token_list[]`` for flow quota action and item support.
+
+
+
 Removed Items
 -------------
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 8b8aa940be..6439de3c1d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 280567a3ae..0068fc275b 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply the quota verdict (PASS or BLOCK) to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /** quota operational mode */
+	int64_t quota;                 /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /** update operation */
+	int64_t quota;                    /** quota value */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v6 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (5 preceding siblings ...)
  2023-01-19 13:25 ` [PATCH v5 " Gregory Etelson
@ 2023-01-19 16:47 ` Gregory Etelson
  2023-01-19 16:47   ` [PATCH v6 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-01-20  8:35   ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
  2023-01-24  9:37 ` [PATCH v7 " Gregory Etelson
                   ` (3 subsequent siblings)
  10 siblings, 2 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-19 16:47 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If indirect action must be conditionally updated according to it's
present state application must first issue action query then
analyze returned data and if needed issue update request.
When the update will be processed, action state can change and
the update can invalidate the action.

The patch adds `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

Application can control query and update order, if that is supported
by port hardware, by setting `qu_mode` parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release release notes.
    Fix doxygen errors.
v4: Add returned errno codes.
v5: Update the patch description.
    Fix typos.
v6: Resolve merge conflict with the main branch.	
---
 doc/guides/rel_notes/release_23_03.rst |  7 ++
 lib/ethdev/rte_flow.c                  | 39 ++++++++++
 lib/ethdev/rte_flow.h                  | 99 ++++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           | 15 ++++
 lib/ethdev/version.map                 |  5 ++
 5 files changed, 165 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index c15f6fbb9f..6941d20abc 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -69,6 +69,13 @@ New Features
     ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
     required for eth_rx, eth_tx, crypto and timer eventdev adapters.
 
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..8b8aa940be 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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 (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..280567a3ae 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,105 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query_update operational mode.
+ *
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before query.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
+	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If update parameter is NULL the function queries indirect action.
+ * If query parameter is NULL the function updates indirect action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update are carried in order
+ * specified in the mode parameter.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   Update profile specification used to modify the action pointed by handle.
+ * @param query
+ *   Pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ * @param 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.
+ * - (ENOTSUP) if underlying device does not support this functionality.
+ * - (EINVAL) - if *handle* invalid
+ */
+__rte_experimental
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   Update profile specification used to modify the action pointed by handle.
+ * @param query
+ *   Pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ * - (ENOTSUP) if underlying device does not support this functionality.
+ * - (EINVAL) - if *handle* invalid
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..628c486081 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# added in 23.03
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v6 2/2] ethdev: add quota flow action and item
  2023-01-19 16:47 ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
@ 2023-01-19 16:47   ` Gregory Etelson
  2023-01-20  8:52     ` Andrew Rybchenko
  2023-01-20  8:35   ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
  1 sibling, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-19 16:47 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Thomas Monjalon, Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

If quota state was BLOCK (negative action tokens number)
application can change it to PASS after providing enough tokens to
raise action tokens number to 0 or above.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release notes.
v5: Update the patch description.
    Remove PMD documentation.
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |  27 ++
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 8 files changed, 672 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 6941d20abc..713b58267f 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -77,6 +77,33 @@ New Features
   - ``rte_flow_action_handle_query_update``
   - ``rte_flow_async_action_handle_query_update``
 
+* **Added quota flow action and quota flow item.**
+
+  Quota action limits traffic according to pre-defined configuration.
+  Quota reflects overall traffic usage regardless bandwidth.
+  Quota flow action initialized with signed tokens number value.
+  Quota flow action updates tokens number according to
+  these rules:
+  * If quota was configured to count packet length, for each packet
+  of size S, tokens number reduced by S.
+  * If quota was configured to count packets, each packet decrements
+  tokens number.
+  Quota action sets packet metadata according to a number of remaining
+  tokens number:
+  * ``PASS`` - remaining tokens number is non-negative.
+  * ``BLOCK`` - remaining tokens number is negative.
+
+  Quota flow item matches on that metadata.
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added tokens to ``token_list[]`` for flow quota action and item support.
+
+
+
 Removed Items
 -------------
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 8b8aa940be..6439de3c1d 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 280567a3ae..0068fc275b 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply the quota verdict (PASS or BLOCK) to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /** quota operational mode */
+	int64_t quota;                 /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /** update operation */
+	int64_t quota;                    /** quota value */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH v6 1/2] ethdev: add query_update sync and async function calls
  2023-01-19 16:47 ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
  2023-01-19 16:47   ` [PATCH v6 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-01-20  8:35   ` Andrew Rybchenko
  2023-01-20 10:46     ` Gregory Etelson
  1 sibling, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-01-20  8:35 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit

On 1/19/23 19:47, Gregory Etelson wrote:
> Current API allows either query or update indirect flow action.
> If indirect action must be conditionally updated according to it's
> present state application must first issue action query then
> analyze returned data and if needed issue update request.
> When the update will be processed, action state can change and
> the update can invalidate the action.
> 
> The patch adds `rte_flow_action_handle_query_update` function call,

"This patch adds ..." -> "Add ..."

> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.

Sorry, may be I'm missing something, but after reading previous
discussion I have a feeling that update could be queried data
dependent. However, I don't understand how it could be possible
with API below. Just my misunderstanding?

> 
> Application can control query and update order, if that is supported
> by port hardware, by setting `qu_mode` parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> ---
> v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> v3: Update release release notes.
>      Fix doxygen errors.
> v4: Add returned errno codes.
> v5: Update the patch description.
>      Fix typos.
> v6: Resolve merge conflict with the main branch.	
> ---
>   doc/guides/rel_notes/release_23_03.rst |  7 ++
>   lib/ethdev/rte_flow.c                  | 39 ++++++++++
>   lib/ethdev/rte_flow.h                  | 99 ++++++++++++++++++++++++++
>   lib/ethdev/rte_flow_driver.h           | 15 ++++
>   lib/ethdev/version.map                 |  5 ++
>   5 files changed, 165 insertions(+)
> 
> diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
> index c15f6fbb9f..6941d20abc 100644
> --- a/doc/guides/rel_notes/release_23_03.rst
> +++ b/doc/guides/rel_notes/release_23_03.rst
> @@ -69,6 +69,13 @@ New Features
>       ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
>       required for eth_rx, eth_tx, crypto and timer eventdev adapters.
>   
> +* **Added functions to atomically query and update indirect flow action.**
> +
> +  Added synchronous and asynchronous functions to atomically query and update
> +  indirect flow action:
> +
> +  - ``rte_flow_action_handle_query_update``
> +  - ``rte_flow_async_action_handle_query_update``

Please, add one more empty line since two should separate
sections.

>   
>   Removed Items
>   -------------
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> index 7d0c24366c..8b8aa940be 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t port_id,
>   					  action_handle, data, user_data, error);
>   	return flow_err(port_id, ret, error);
>   }
> +
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode mode,
> +				    struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];

I know that nearby code does the same, but I still dislike it.
If port_id is invalid, it could be out-of-boundary access.
Yes, port_id is checked on ops get, but it would be safer to
do the assignment after ops check below.
Right now there is no bugs, but the future modifications
could break it.

> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +	int ret;
> +
> +	if (!ops || !ops->action_handle_query_update)
> +		return -ENOTSUP;
> +	ret = ops->action_handle_query_update(dev, handle, update, query,
> +					      mode, error);
> +	return flow_err(port_id, ret, error);
> +}
> +
> +int
> +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle *handle,
> +					  const void *update, void *query,
> +					  enum rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  struct rte_flow_error *error)
> +{
> +	struct rte_eth_dev *dev = &rte_eth_devices[port_id];

same here

> +	const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> +	int ret;
> +
> +	if (!ops || !ops->async_action_handle_query_update)
> +		return -ENOTSUP;
> +	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
> +						    handle, update, query, mode,
> +						    user_data, error);
> +	return flow_err(port_id, ret, error);
> +}
> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index b60987db4b..280567a3ae 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -5622,6 +5622,105 @@ rte_flow_async_action_handle_query(uint16_t port_id,
>   		void *user_data,
>   		struct rte_flow_error *error);
>   
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query_update operational mode.

May be "Query and update operation mode."?

> + *
> + * RTE_FLOW_QU_QUERY_FIRST
> + *   Force port to query action before update.
> + * RTE_FLOW_QU_UPDATE_FIRST
> + *   Force port to update action before query.
> + *
> + * @see rte_flow_action_handle_query_update()
> + * @see rte_flow_async_action_handle_query_update()
> + */
> +enum rte_flow_query_update_mode {
> +	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
> +	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and/or update indirect flow action.
> + * If update parameter is NULL the function queries indirect action.
> + * If query parameter is NULL the function updates indirect action.

IMHO it is better to highlight in the parameter description
below that parameter may be NULL. See below.

> + * If both query and update not NULL, the function atomically
> + * queries and updates indirect action. Query and update are carried in order
> + * specified in the mode parameter.

I'd like to double-check what happens if both ops are NULL.
I guess it is just NOP.
Also it should not be a problem is QUERY_FIRST is specified,
but query is NULL. Same for update.
If yes in both cases, no extra documentation is required.
If it is an error, it should be documented.

> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param update
> + *   Update profile specification used to modify the action pointed by handle.

If not NULL, update ...

> + * @param query
> + *   Pointer to storage for the associated query data type.

If not NULL, query action data and store in provided location.

> + * @param mode
> + *   Operational mode.
> + * @param 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.
> + * - (ENOTSUP) if underlying device does not support this functionality.
> + * - (EINVAL) - if *handle* invalid
> + */
> +__rte_experimental
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode mode,
> +				    struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue async indirect flow action query and/or update
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue which is used to update the rule.
> + * @param attr
> + *   Indirect action update operation attributes.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param update
> + *   Update profile specification used to modify the action pointed by handle.

It should be documented that NULL is allowed

> + * @param query
> + *   Pointer to storage for the associated query data type.
> + *   Query result returned on async completion event.

It should be documented that NULL is allowed

> + * @param mode
> + *   Operational mode.
> + * @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 on success, a negative errno value otherwise and rte_errno is set.
> + * - (ENOTSUP) if underlying device does not support this functionality.
> + * - (EINVAL) - if *handle* invalid
> + */
> +__rte_experimental
> +int
> +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle *handle,
> +					  const void *update, void *query,
> +					  enum rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  struct rte_flow_error *error);
> +
>   #ifdef __cplusplus
>   }
>   #endif

[skip]


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v6 2/2] ethdev: add quota flow action and item
  2023-01-19 16:47   ` [PATCH v6 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-01-20  8:52     ` Andrew Rybchenko
  2023-01-24  9:26       ` Gregory Etelson
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-01-20  8:52 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Aman Singh, Yuying Zhang, Ferruh Yigit,
	Thomas Monjalon

On 1/19/23 19:47, Gregory Etelson wrote:
> Quota action limits traffic according to pre-defined configuration.
> Quota reflects overall traffic usage regardless bandwidth.
> Quota flow action initialized with signed tokens number value.
> Quota flow action updates tokens number according to
> these rules:
> 1. if quota was configured to count packet length, for each packet
>     of size S, tokens number reduced by S.
> 2. If quota was configured to count packets, each packet decrements
>     tokens number.
> quota action sets packet metadata according to a number of remaining
> tokens number:
>    PASS - remaining tokens number is non-negative.
>    BLOCK - remaining tokens number is negative.
> 
> Quota flow item matches on that data
> 
> Application updates tokens number in quota flow action
> with SET or ADD calls:
>   SET(QUOTA, val) - arm quota with new tokens number set to val
>   ADD(QUOTA, val) - increase existing quota tokens number by val
> 
> Both SET and ADD return to application number of tokens stored in port
> before update.
> 
> If quota state was BLOCK (negative action tokens number)
> application can change it to PASS after providing enough tokens to
> raise action tokens number to 0 or above.
> 
> Application must create a rule with quota action to mark flow and
> match on the mark with quota item in following flow rule.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>

[snip]


> diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
> index 6941d20abc..713b58267f 100644
> --- a/doc/guides/rel_notes/release_23_03.rst
> +++ b/doc/guides/rel_notes/release_23_03.rst
> @@ -77,6 +77,33 @@ New Features
>     - ``rte_flow_action_handle_query_update``
>     - ``rte_flow_async_action_handle_query_update``
>   
> +* **Added quota flow action and quota flow item.**
> +
> +  Quota action limits traffic according to pre-defined configuration.
> +  Quota reflects overall traffic usage regardless bandwidth.
> +  Quota flow action initialized with signed tokens number value.
> +  Quota flow action updates tokens number according to
> +  these rules:
> +  * If quota was configured to count packet length, for each packet
> +  of size S, tokens number reduced by S.
> +  * If quota was configured to count packets, each packet decrements
> +  tokens number.
> +  Quota action sets packet metadata according to a number of remaining
> +  tokens number:
> +  * ``PASS`` - remaining tokens number is non-negative.
> +  * ``BLOCK`` - remaining tokens number is negative.
> +
> +  Quota flow item matches on that metadata.
> +
> +  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
> +  - ``RTE_FLOW_ITEM_TYPE_QUOTA``

Quota description should be a part of action/item
documentation, not release notes.

> +
> +* **Updated testpmd to support quota flow action and item.**
> +
> +  Added tokens to ``token_list[]`` for flow quota action and item support.

IMHO 'token_list' is inapropriate in release notes. It is too
deep technical detail.

> +
> +
> +
>   Removed Items
>   -------------
>   

> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index 280567a3ae..0068fc275b 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -624,7 +624,45 @@ enum rte_flow_item_type {
>   	 * See struct rte_flow_item_meter_color.
>   	 */
>   	RTE_FLOW_ITEM_TYPE_METER_COLOR,
> +
> +	/**
> +	 * Match Quota state
> +	 *
> +	 * @see struct rte_flow_item_quota
> +	 */
> +	 RTE_FLOW_ITEM_TYPE_QUOTA,
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * QUOTA state.
> + *
> + * @see struct rte_flow_item_quota
> + */
> +enum rte_flow_quota_state {
> +	RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
> +	RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */

Enum memeber comments should start from /**< since it is a
documentation after documented code.

> +};
> +
> +/**
> + * RTE_FLOW_ITEM_TYPE_QUOTA
> + *
> + * Matches QUOTA state
> + */
> +struct rte_flow_item_quota {
> +	enum rte_flow_quota_state state;
> +};
> +
> +/**
> + * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
> + */
> +#ifndef __cplusplus
> +static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
> +	.state = (enum rte_flow_quota_state)0xff

I don't understand why it is just 0xff, not 0x1, not 0xf,
not 0xffff, not 0xffffffff.

Isn't it better to make 'state' -> 'state_mask' and use
PASS and BLOCK as corresponding bits in a mask. If so,
here should be a value with all known bits set.

>   };
> +#endif
>   
>   /**
>    *
> @@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
>   	 * No associated configuration structure.
>   	 */
>   	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
> +
> +	/**
> +	 * Apply the quota verdict (PASS or BLOCK) to a flow.
> +	 *
> +	 * @see struct rte_flow_action_quota
> +	 * @see struct rte_flow_query_quota
> +	 * @see struct rte_flow_update_quota
> +	 */
> +	 RTE_FLOW_ACTION_TYPE_QUOTA,
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * QUOTA operational mode.
> + *
> + * @see struct rte_flow_action_quota
> + */
> +enum rte_flow_quota_mode {
> +	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */

/**<

> +	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */

/**<

> +	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */

/**<

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Create QUOTA action.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + */
> +struct rte_flow_action_quota {
> +	enum rte_flow_quota_mode mode; /** quota operational mode */

/**<

> +	int64_t quota;                 /** quota value */

/**<

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query indirect QUOTA action.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + *
> + */
> +struct rte_flow_query_quota {
> +	int64_t quota; /** quota value */

/**<

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Indirect QUOTA update operations.
> + *
> + * @see struct rte_flow_update_quota
> + */
> +enum rte_flow_update_quota_op {
> +	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */

/**<

> +	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */

/**<

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + *
> + * Update indirect QUOTA action.
> + */
> +struct rte_flow_update_quota {
> +	enum rte_flow_update_quota_op op; /** update operation */

/**<

> +	int64_t quota;                    /** quota value */

/**<

>   };
>   
>   /**
> @@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
>   	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
>   	 */
>   	uint32_t max_nb_conn_tracks;
> +	/**
> +	 * Maximum number of quota actions.
> +	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
> +	 */
> +	uint32_t max_nb_quotas;
>   	/**
>   	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
>   	 */
> @@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
>   	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
>   	 */
>   	uint32_t nb_conn_tracks;
> +	/**
> +	 * Maximum number of quota actions.
> +	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
> +	 */
> +	uint32_t nb_quotas;
>   	/**
>   	 * Port flags (RTE_FLOW_PORT_FLAG_*).
>   	 */


^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v6 1/2] ethdev: add query_update sync and async function calls
  2023-01-20  8:35   ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
@ 2023-01-20 10:46     ` Gregory Etelson
  2023-01-20 11:22       ` Andrew Rybchenko
  0 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-20 10:46 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

Hello Andrew,

[]

> > The patch adds `rte_flow_action_handle_query_update` function call,
> 
> "This patch adds ..." -> "Add ..."
> 

Will fix in v7.

> > and it's async version `rte_flow_async_action_handle_query_update`
> > to atomically query and update flow action.
> 
> Sorry, may be I'm missing something, but after reading previous
> discussion I have a feeling that update could be queried data
> dependent. However, I don't understand how it could be possible
> with API below. Just my misunderstanding?
> 

The goal of `rte_flow_action_handle_query_update()` is to execute query and update actions atomically.
The function guaranties that action state will not change by any event before both update and query complete.
If the function was called with the ` RTE_FLOW_QU_QUERY_FIRST ` `mode` argument, then update execution can depend
on query result. That's up to query format, PMD implementation and hardware capabilities.
I hope that answered your question.
  
> >
> > Application can control query and update order, if that is supported
> > by port hardware, by setting `qu_mode` parameter to
> > RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> >
> > Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> > ---
> > v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> > v3: Update release release notes.
> >      Fix doxygen errors.
> > v4: Add returned errno codes.
> > v5: Update the patch description.
> >      Fix typos.
> > v6: Resolve merge conflict with the main branch.
> > ---
> >   doc/guides/rel_notes/release_23_03.rst |  7 ++
> >   lib/ethdev/rte_flow.c                  | 39 ++++++++++
> >   lib/ethdev/rte_flow.h                  | 99 ++++++++++++++++++++++++++
> >   lib/ethdev/rte_flow_driver.h           | 15 ++++
> >   lib/ethdev/version.map                 |  5 ++
> >   5 files changed, 165 insertions(+)
> >
> > diff --git a/doc/guides/rel_notes/release_23_03.rst
> b/doc/guides/rel_notes/release_23_03.rst
> > index c15f6fbb9f..6941d20abc 100644
> > --- a/doc/guides/rel_notes/release_23_03.rst
> > +++ b/doc/guides/rel_notes/release_23_03.rst
> > @@ -69,6 +69,13 @@ New Features
> >       ``rte_event_dev_config::nb_single_link_event_port_queues``
> parameter
> >       required for eth_rx, eth_tx, crypto and timer eventdev adapters.
> >
> > +* **Added functions to atomically query and update indirect flow
> action.**
> > +
> > +  Added synchronous and asynchronous functions to atomically query
> and update
> > +  indirect flow action:
> > +
> > +  - ``rte_flow_action_handle_query_update``
> > +  - ``rte_flow_async_action_handle_query_update``
> 
> Please, add one more empty line since two should separate
> sections.
> 

Will fix in v7.

> >
> >   Removed Items
> >   -------------
> > diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> > index 7d0c24366c..8b8aa940be 100644
> > --- a/lib/ethdev/rte_flow.c
> > +++ b/lib/ethdev/rte_flow.c
> > @@ -1883,3 +1883,42 @@ rte_flow_async_action_handle_query(uint16_t
> port_id,
> >                                         action_handle, data, user_data, error);
> >       return flow_err(port_id, ret, error);
> >   }
> > +
> > +int
> > +rte_flow_action_handle_query_update(uint16_t port_id,
> > +                                 struct rte_flow_action_handle *handle,
> > +                                 const void *update, void *query,
> > +                                 enum rte_flow_query_update_mode mode,
> > +                                 struct rte_flow_error *error)
> > +{
> > +     struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> 
> I know that nearby code does the same, but I still dislike it.
> If port_id is invalid, it could be out-of-boundary access.
> Yes, port_id is checked on ops get, but it would be safer to
> do the assignment after ops check below.
> Right now there is no bugs, but the future modifications
> could break it.
> 

Will fix in v7.

> > +     const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +     int ret;
> > +
> > +     if (!ops || !ops->action_handle_query_update)
> > +             return -ENOTSUP;
> > +     ret = ops->action_handle_query_update(dev, handle, update, query,
> > +                                           mode, error);
> > +     return flow_err(port_id, ret, error);
> > +}
> > +
> > +int
> > +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> > +                                       const struct rte_flow_op_attr *attr,
> > +                                       struct rte_flow_action_handle *handle,
> > +                                       const void *update, void *query,
> > +                                       enum rte_flow_query_update_mode mode,
> > +                                       void *user_data,
> > +                                       struct rte_flow_error *error)
> > +{
> > +     struct rte_eth_dev *dev = &rte_eth_devices[port_id];
> 
> same here
> 

Will fix in v7.

> > +     const struct rte_flow_ops *ops = rte_flow_ops_get(port_id, error);
> > +     int ret;
> > +
> > +     if (!ops || !ops->async_action_handle_query_update)
> > +             return -ENOTSUP;
> > +     ret = ops->async_action_handle_query_update(dev, queue_id, attr,
> > +                                                 handle, update, query, mode,
> > +                                                 user_data, error);
> > +     return flow_err(port_id, ret, error);
> > +}
> > diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> > index b60987db4b..280567a3ae 100644
> > --- a/lib/ethdev/rte_flow.h
> > +++ b/lib/ethdev/rte_flow.h
> > @@ -5622,6 +5622,105 @@
> rte_flow_async_action_handle_query(uint16_t port_id,
> >               void *user_data,
> >               struct rte_flow_error *error);
> >
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query_update operational mode.
> 
> May be "Query and update operation mode."?
> 

Looks good. Will fix in v7.

> > + *
> > + * RTE_FLOW_QU_QUERY_FIRST
> > + *   Force port to query action before update.
> > + * RTE_FLOW_QU_UPDATE_FIRST
> > + *   Force port to update action before query.
> > + *
> > + * @see rte_flow_action_handle_query_update()
> > + * @see rte_flow_async_action_handle_query_update()
> > + */
> > +enum rte_flow_query_update_mode {
> > +     RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
> > +     RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query and/or update indirect flow action.
> > + * If update parameter is NULL the function queries indirect action.
> > + * If query parameter is NULL the function updates indirect action.
> 
> IMHO it is better to highlight in the parameter description
> below that parameter may be NULL. See below.
> 

Will fix in v7.

> > + * If both query and update not NULL, the function atomically
> > + * queries and updates indirect action. Query and update are carried in
> order
> > + * specified in the mode parameter.
> 
> I'd like to double-check what happens if both ops are NULL.
> I guess it is just NOP.

If both query and update parameters were NULL, the function will fail with EINVAL error
Will fix in v7.

> Also it should not be a problem is QUERY_FIRST is specified,
> but query is NULL. Same for update.
> If yes in both cases, no extra documentation is required.
> If it is an error, it should be documented.
> 

Both RTE_FLOW_QU_QUERY_FIRST and RTE_FLOW_QU_UPDATE_FIRST require
query and update parameters.
Will fix in v7.

> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param update
> > + *   Update profile specification used to modify the action pointed by
> handle.
> 
> If not NULL, update ...
> 

Will fix in v7.

> > + * @param query
> > + *   Pointer to storage for the associated query data type.
> 
> If not NULL, query action data and store in provided location.
> 

Will fix in v7.

> > + * @param mode
> > + *   Operational mode.
> > + * @param 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.
> > + * - (ENOTSUP) if underlying device does not support this functionality.
> > + * - (EINVAL) - if *handle* invalid
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_action_handle_query_update(uint16_t port_id,
> > +                                 struct rte_flow_action_handle *handle,
> > +                                 const void *update, void *query,
> > +                                 enum rte_flow_query_update_mode mode,
> > +                                 struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue async indirect flow action query and/or update
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue which is used to update the rule.
> > + * @param attr
> > + *   Indirect action update operation attributes.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param update
> > + *   Update profile specification used to modify the action pointed by
> handle.
> 
> It should be documented that NULL is allowed
> 

Will fix in v7.

> > + * @param query
> > + *   Pointer to storage for the associated query data type.
> > + *   Query result returned on async completion event.
> 
> It should be documented that NULL is allowed
> 

Will fix in v7.

> > + * @param mode
> > + *   Operational mode.
> > + * @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 on success, a negative errno value otherwise and rte_errno is set.
> > + * - (ENOTSUP) if underlying device does not support this functionality.
> > + * - (EINVAL) - if *handle* invalid
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> > +                                       const struct rte_flow_op_attr *attr,
> > +                                       struct rte_flow_action_handle *handle,
> > +                                       const void *update, void *query,
> > +                                       enum rte_flow_query_update_mode mode,
> > +                                       void *user_data,
> > +                                       struct rte_flow_error *error);
> > +
> >   #ifdef __cplusplus
> >   }
> >   #endif
> 
> [skip]


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v6 1/2] ethdev: add query_update sync and async function calls
  2023-01-20 10:46     ` Gregory Etelson
@ 2023-01-20 11:22       ` Andrew Rybchenko
  2023-01-20 16:50         ` Gregory Etelson
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-01-20 11:22 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

On 1/20/23 13:46, Gregory Etelson wrote:
>>> and it's async version `rte_flow_async_action_handle_query_update`
>>> to atomically query and update flow action.
>>
>> Sorry, may be I'm missing something, but after reading previous
>> discussion I have a feeling that update could be queried data
>> dependent. However, I don't understand how it could be possible
>> with API below. Just my misunderstanding?
>>
> 
> The goal of `rte_flow_action_handle_query_update()` is to execute query and update actions atomically.
> The function guaranties that action state will not change by any event before both update and query complete.
> If the function was called with the ` RTE_FLOW_QU_QUERY_FIRST ` `mode` argument, then update execution can depend
> on query result. That's up to query format, PMD implementation and hardware capabilities.
> I hope that answered your question.

Sorry, I'm still confused. Could you explain a bit more,
please. How update could depend on query result?
Caller already specified update structure...


^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v6 1/2] ethdev: add query_update sync and async function calls
  2023-01-20 11:22       ` Andrew Rybchenko
@ 2023-01-20 16:50         ` Gregory Etelson
  2023-02-01 11:00           ` Andrew Rybchenko
  0 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-20 16:50 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

Hello Andrew,

[]

> On 1/20/23 13:46, Gregory Etelson wrote:
> >>> and it's async version `rte_flow_async_action_handle_query_update`
> >>> to atomically query and update flow action.
> >>
> >> Sorry, may be I'm missing something, but after reading previous
> >> discussion I have a feeling that update could be queried data
> >> dependent. However, I don't understand how it could be possible
> >> with API below. Just my misunderstanding?
> >>
> >
> > The goal of `rte_flow_action_handle_query_update()` is to execute query
> and update actions atomically.
> > The function guaranties that action state will not change by any event
> before both update and query complete.
> > If the function was called with the ` RTE_FLOW_QU_QUERY_FIRST `
> `mode` argument, then update execution can depend
> > on query result. That's up to query format, PMD implementation and
> hardware capabilities.
> > I hope that answered your question.
> 
> Sorry, I'm still confused. Could you explain a bit more,
> please. How update could depend on query result?

Conditional update I described requires special action properties.
Consider an action object with a method that receives query and update as parameters.
The method will activate update only if query result satisfies action state.
If the action was updated, both query and update were atomic for application.
The function returns the action state - updated or not.
Application is aware about the action properties.
Application applies action properties to returned state to discover if action was updated.

> Caller already specified update structure...
 
Regards,
Gregory

^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v6 2/2] ethdev: add quota flow action and item
  2023-01-20  8:52     ` Andrew Rybchenko
@ 2023-01-24  9:26       ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-24  9:26 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, NBU-Contact-Thomas Monjalon (EXTERNAL)

Hello Andrew,

[]

> > +  Quota action sets packet metadata according to a number of remaining
> > +  tokens number:
> > +  * ``PASS`` - remaining tokens number is non-negative.
> > +  * ``BLOCK`` - remaining tokens number is negative.
> > +
> > +  Quota flow item matches on that metadata.
> > +
> > +  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
> > +  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
> 
> Quota description should be a part of action/item
> documentation, not release notes.
>

Will fix in v7.
 
> > +
> > +* **Updated testpmd to support quota flow action and item.**
> > +
> > +  Added tokens to ``token_list[]`` for flow quota action and item support.
> 
> IMHO 'token_list' is inapropriate in release notes. It is too
> deep technical detail.
> 

Will fix in v7.

> > +
> > +
> > +
> >   Removed Items
> >   -------------
> >
> 
> > diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> > index 280567a3ae..0068fc275b 100644
> > --- a/lib/ethdev/rte_flow.h
> > +++ b/lib/ethdev/rte_flow.h
> > @@ -624,7 +624,45 @@ enum rte_flow_item_type {
> >        * See struct rte_flow_item_meter_color.
> >        */
> >       RTE_FLOW_ITEM_TYPE_METER_COLOR,
> > +
> > +     /**
> > +      * Match Quota state
> > +      *
> > +      * @see struct rte_flow_item_quota
> > +      */
> > +      RTE_FLOW_ITEM_TYPE_QUOTA,
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * QUOTA state.
> > + *
> > + * @see struct rte_flow_item_quota
> > + */
> > +enum rte_flow_quota_state {
> > +     RTE_FLOW_QUOTA_STATE_PASS, /** PASS quota state */
> > +     RTE_FLOW_QUOTA_STATE_BLOCK /** BLOCK quota state */
> 
> Enum memeber comments should start from /**< since it is a
> documentation after documented code.
>

Will fix in v7.
 
> > +};
> > +
> > +/**
> > + * RTE_FLOW_ITEM_TYPE_QUOTA
> > + *
> > + * Matches QUOTA state
> > + */
> > +struct rte_flow_item_quota {
> > +     enum rte_flow_quota_state state;
> > +};
> > +
> > +/**
> > + * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
> > + */
> > +#ifndef __cplusplus
> > +static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
> > +     .state = (enum rte_flow_quota_state)0xff
> 
> I don't understand why it is just 0xff, not 0x1, not 0xf,
> not 0xffff, not 0xffffffff.
> 
> Isn't it better to make 'state' -> 'state_mask' and use
> PASS and BLOCK as corresponding bits in a mask. If so,
> here should be a value with all known bits set.
> 

Static default masks in rte_flow.h set all relevant bits.
That's the general cross-hardware convention.

> >   };
> > +#endif
> >
> >   /**
> >    *
> > @@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
> >        * No associated configuration structure.
> >        */
> >       RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
> > +
> > +     /**
> > +      * Apply the quota verdict (PASS or BLOCK) to a flow.
> > +      *
> > +      * @see struct rte_flow_action_quota
> > +      * @see struct rte_flow_query_quota
> > +      * @see struct rte_flow_update_quota
> > +      */
> > +      RTE_FLOW_ACTION_TYPE_QUOTA,
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * QUOTA operational mode.
> > + *
> > + * @see struct rte_flow_action_quota
> > + */
> > +enum rte_flow_quota_mode {
> > +     RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
> 
> /**<
> 
> > +     RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting
> from L2 */
> 
> /**<
> 
> > +     RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting
> from L3 */
> 
> /**<
> 
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Create QUOTA action.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + */
> > +struct rte_flow_action_quota {
> > +     enum rte_flow_quota_mode mode; /** quota operational mode */
> 
> /**<
> 
> > +     int64_t quota;                 /** quota value */
> 
> /**<
> 
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query indirect QUOTA action.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + *
> > + */
> > +struct rte_flow_query_quota {
> > +     int64_t quota; /** quota value */
> 
> /**<
> 
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Indirect QUOTA update operations.
> > + *
> > + * @see struct rte_flow_update_quota
> > + */
> > +enum rte_flow_update_quota_op {
> > +     RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
> 
> /**<
> 
> > +     RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with
> new value */
> 
> /**<
> 
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + *
> > + * Update indirect QUOTA action.
> > + */
> > +struct rte_flow_update_quota {
> > +     enum rte_flow_update_quota_op op; /** update operation */
> 
> /**<
> 
> > +     int64_t quota;                    /** quota value */
> 
> /**<
> 
> >   };
> >
> >   /**
> > @@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
> >        * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
> >        */
> >       uint32_t max_nb_conn_tracks;
> > +     /**
> > +      * Maximum number of quota actions.
> > +      * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > +      */
> > +     uint32_t max_nb_quotas;
> >       /**
> >        * Port supported flags (RTE_FLOW_PORT_FLAG_*).
> >        */
> > @@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
> >        * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
> >        */
> >       uint32_t nb_conn_tracks;
> > +     /**
> > +      * Maximum number of quota actions.
> > +      * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > +      */
> > +     uint32_t nb_quotas;
> >       /**
> >        * Port flags (RTE_FLOW_PORT_FLAG_*).
> >        */


^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v7 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (6 preceding siblings ...)
  2023-01-19 16:47 ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
@ 2023-01-24  9:37 ` Gregory Etelson
  2023-01-24  9:37   ` [PATCH v7 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-02-01 11:16   ` [PATCH v7 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
  2023-02-01 15:16 ` [PATCH v8 " Gregory Etelson
                   ` (2 subsequent siblings)
  10 siblings, 2 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-01-24  9:37 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If indirect action must be conditionally updated according to it's
present state application must first issue action query then
analyze returned data and if needed issue update request.
When the update will be processed, action state can change and
the update can invalidate the action.

Add `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

Application can control query and update order, if that is supported
by port hardware, by setting `qu_mode` parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release release notes.
    Fix doxygen errors.
v4: Add returned errno codes.
v5: Update the patch description.
    Fix typos.
v6: Resolve merge conflict with the main branch.	
v7: Update documentation.
---
 doc/guides/rel_notes/release_23_03.rst |   7 ++
 lib/ethdev/rte_flow.c                  |  45 +++++++++++
 lib/ethdev/rte_flow.h                  | 103 +++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  15 ++++
 lib/ethdev/version.map                 |   5 ++
 5 files changed, 175 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index c15f6fbb9f..5b98e18032 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -68,6 +68,13 @@ New Features
   * With this change, the application no longer needs to account for the
     ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
     required for eth_rx, eth_tx, crypto and timer eventdev adapters.
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
 
 
 Removed Items
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..ece40e7877 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,48 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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);
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  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);
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..6705cb32af 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,109 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and update operational mode.
+ *
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before query.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
+	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update are carried in order
+ * specified in the mode parameter.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ *   Required if both *update* and *query* are not NULL.
+ * @param 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.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) - if *handle* invalid or both *query* and *update* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL, pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ *   Required if both *update* and *query* are not NULL.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) - if *handle* invalid or both *update* and *query* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..628c486081 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# added in 23.03
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v7 2/2] ethdev: add quota flow action and item
  2023-01-24  9:37 ` [PATCH v7 " Gregory Etelson
@ 2023-01-24  9:37   ` Gregory Etelson
  2023-02-01 11:22     ` Andrew Rybchenko
  2023-02-01 11:16   ` [PATCH v7 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
  1 sibling, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-01-24  9:37 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Thomas Monjalon, Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

If quota state was BLOCK (negative action tokens number)
application can change it to PASS after providing enough tokens to
raise action tokens number to 0 or above.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release notes.
v5: Update the patch description.
    Remove PMD documentation.
v7: Update documentation.
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |  12 +
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 8 files changed, 657 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 5b98e18032..0756cca241 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -77,6 +77,18 @@ New Features
   - ``rte_flow_async_action_handle_query_update``
 
 
+* **Added quota flow action and quota flow item.**
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added support for flow quota action and item.
+
+
+
 Removed Items
 -------------
 
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index ece40e7877..448cf1607e 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index 6705cb32af..46409ed607 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /**< PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /**< BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply the quota verdict (PASS or BLOCK) to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /** quota operational mode */
+	int64_t quota;                 /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /** quota value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /** update operation */
+	int64_t quota;                    /** quota value */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH v6 1/2] ethdev: add query_update sync and async function calls
  2023-01-20 16:50         ` Gregory Etelson
@ 2023-02-01 11:00           ` Andrew Rybchenko
  2023-02-01 14:03             ` Gregory Etelson
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-01 11:00 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

On 1/20/23 19:50, Gregory Etelson wrote:
> Hello Andrew,
> 
> []
> 
>> On 1/20/23 13:46, Gregory Etelson wrote:
>>>>> and it's async version `rte_flow_async_action_handle_query_update`
>>>>> to atomically query and update flow action.
>>>>
>>>> Sorry, may be I'm missing something, but after reading previous
>>>> discussion I have a feeling that update could be queried data
>>>> dependent. However, I don't understand how it could be possible
>>>> with API below. Just my misunderstanding?
>>>>
>>>
>>> The goal of `rte_flow_action_handle_query_update()` is to execute query
>> and update actions atomically.
>>> The function guaranties that action state will not change by any event
>> before both update and query complete.
>>> If the function was called with the ` RTE_FLOW_QU_QUERY_FIRST `
>> `mode` argument, then update execution can depend
>>> on query result. That's up to query format, PMD implementation and
>> hardware capabilities.
>>> I hope that answered your question.
>>
>> Sorry, I'm still confused. Could you explain a bit more,
>> please. How update could depend on query result?
> 
> Conditional update I described requires special action properties.
> Consider an action object with a method that receives query and update as parameters.
> The method will activate update only if query result satisfies action state.
> If the action was updated, both query and update were atomic for application.
> The function returns the action state - updated or not.
> Application is aware about the action properties.
> Application applies action properties to returned state to discover if action was updated.

Is it just a theoretical possibility or do you have an example
right now?

> 
>> Caller already specified update structure...
>   
> Regards,
> Gregory


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 1/2] ethdev: add query_update sync and async function calls
  2023-01-24  9:37 ` [PATCH v7 " Gregory Etelson
  2023-01-24  9:37   ` [PATCH v7 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-02-01 11:16   ` Andrew Rybchenko
  2023-02-01 14:52     ` Gregory Etelson
  1 sibling, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-01 11:16 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit

On 1/24/23 12:37, Gregory Etelson wrote:
> Current API allows either query or update indirect flow action.
> If indirect action must be conditionally updated according to it's
> present state application must first issue action query then
> analyze returned data and if needed issue update request.
> When the update will be processed, action state can change and
> the update can invalidate the action.
> 
> Add `rte_flow_action_handle_query_update` function call,
> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.
> 
> Application can control query and update order, if that is supported
> by port hardware, by setting `qu_mode` parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> ---
> v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> v3: Update release release notes.
>      Fix doxygen errors.
> v4: Add returned errno codes.
> v5: Update the patch description.
>      Fix typos.
> v6: Resolve merge conflict with the main branch.	
> v7: Update documentation.
> ---
>   doc/guides/rel_notes/release_23_03.rst |   7 ++
>   lib/ethdev/rte_flow.c                  |  45 +++++++++++
>   lib/ethdev/rte_flow.h                  | 103 +++++++++++++++++++++++++
>   lib/ethdev/rte_flow_driver.h           |  15 ++++
>   lib/ethdev/version.map                 |   5 ++
>   5 files changed, 175 insertions(+)
> 
> diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
> index c15f6fbb9f..5b98e18032 100644
> --- a/doc/guides/rel_notes/release_23_03.rst
> +++ b/doc/guides/rel_notes/release_23_03.rst
> @@ -68,6 +68,13 @@ New Features
>     * With this change, the application no longer needs to account for the
>       ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
>       required for eth_rx, eth_tx, crypto and timer eventdev adapters.

empty line is required here

> +* **Added functions to atomically query and update indirect flow action.**
> +
> +  Added synchronous and asynchronous functions to atomically query and update
> +  indirect flow action:
> +
> +  - ``rte_flow_action_handle_query_update``
> +  - ``rte_flow_async_action_handle_query_update``
>   
>   
>   Removed Items

[snip]

> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index b60987db4b..6705cb32af 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -5622,6 +5622,109 @@ rte_flow_async_action_handle_query(uint16_t port_id,
>   		void *user_data,
>   		struct rte_flow_error *error);
>   
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and update operational mode.
> + *
> + * RTE_FLOW_QU_QUERY_FIRST
> + *   Force port to query action before update.
> + * RTE_FLOW_QU_UPDATE_FIRST
> + *   Force port to update action before query.

Do not duplicate enum members.

> + *
> + * @see rte_flow_action_handle_query_update()
> + * @see rte_flow_async_action_handle_query_update()
> + */
> +enum rte_flow_query_update_mode {
> +	RTE_FLOW_QU_QUERY_FIRST,  /* query before update */

/**< to document enum member

/**< Force port to query action before update. */

if it fits line length or before the enum member:

/** Force port to query action before update. */

> +	RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */

same

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and/or update indirect flow action.
> + * If both query and update not NULL, the function atomically
> + * queries and updates indirect action. Query and update are carried in order
> + * specified in the mode parameter.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param update
> + *   If not NULL, update profile specification used to modify the action
> + *   pointed by handle.
> + * @param query
> + *   If not NULL pointer to storage for the associated query data type.
> + * @param mode
> + *   Operational mode.
> + *   Required if both *update* and *query* are not NULL.
> + * @param 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.
> + * - (-ENODEV) if *port_id* invalid.
> + * - (-ENOTSUP) if underlying device does not support this functionality.
> + * - (-EINVAL) - if *handle* invalid or both *query* and *update* are NULL.

Be consistent. You have no '-' before if in two cases above and
have in the EINVAL case.

> + */
> +__rte_experimental
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode mode,
> +				    struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue async indirect flow action query and/or update
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue which is used to update the rule.
> + * @param attr
> + *   Indirect action update operation attributes.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param update
> + *   If not NULL, update profile specification used to modify the action
> + *   pointed by handle.
> + * @param query
> + *   If not NULL, pointer to storage for the associated query data type.
> + *   Query result returned on async completion event.
> + * @param mode
> + *   Operational mode.
> + *   Required if both *update* and *query* are not NULL.
> + * @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 on success, a negative errno value otherwise and rte_errno is set.
> + * - (-ENODEV) if *port_id* invalid.
> + * - (-ENOTSUP) if underlying device does not support this functionality.
> + * - (-EINVAL) - if *handle* invalid or both *update* and *query* are NULL.

same

> + */
> +__rte_experimental
> +int
> +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle *handle,
> +					  const void *update, void *query,
> +					  enum rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  struct rte_flow_error *error);
> +
>   #ifdef __cplusplus
>   }
>   #endif

[snip]


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v7 2/2] ethdev: add quota flow action and item
  2023-01-24  9:37   ` [PATCH v7 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-02-01 11:22     ` Andrew Rybchenko
  2023-02-01 15:09       ` Gregory Etelson
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-01 11:22 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Aman Singh, Yuying Zhang, Ferruh Yigit,
	Thomas Monjalon

On 1/24/23 12:37, Gregory Etelson wrote:
> Quota action limits traffic according to pre-defined configuration.
> Quota reflects overall traffic usage regardless bandwidth.
> Quota flow action initialized with signed tokens number value.
> Quota flow action updates tokens number according to
> these rules:
> 1. if quota was configured to count packet length, for each packet
>     of size S, tokens number reduced by S.
> 2. If quota was configured to count packets, each packet decrements
>     tokens number.
> quota action sets packet metadata according to a number of remaining
> tokens number:
>    PASS - remaining tokens number is non-negative.
>    BLOCK - remaining tokens number is negative.
> 
> Quota flow item matches on that data
> 
> Application updates tokens number in quota flow action
> with SET or ADD calls:
>   SET(QUOTA, val) - arm quota with new tokens number set to val
>   ADD(QUOTA, val) - increase existing quota tokens number by val
> 
> Both SET and ADD return to application number of tokens stored in port
> before update.
> 
> If quota state was BLOCK (negative action tokens number)
> application can change it to PASS after providing enough tokens to
> raise action tokens number to 0 or above.
> 
> Application must create a rule with quota action to mark flow and
> match on the mark with quota item in following flow rule.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>

[snip]

> diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
> index 3e6242803d..7a3868638c 100644
> --- a/doc/guides/prog_guide/rte_flow.rst
> +++ b/doc/guides/prog_guide/rte_flow.rst
> @@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
>   
>   - ``color``: Metering color marker.
>   
> +Item: ``QUOTA``
> +^^^^^^^^^^^^^^^
> +
> +Matches flow quota state set by quota action.
> +
> +- ``state``: Flow quota state
> +
>   Actions
>   ~~~~~~~
>   
> @@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
>      | ``policy``       | Meter policy object  |
>      +------------------+----------------------+
>   
> +Action: ``QUOTA``
> +^^^^^^^^^^^^^^^^^
> +
> +Update ``quota`` value and set packet quota state.
> +If the ``quota`` value after update is non-negative packet quota state set to
> +``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
> +The ``quota`` value is reduced according to ``mode`` setting.
> +
> +.. _table_rte_flow_action_quota:
> +
> +.. table:: QUOTA
> +
> +   +------------------+------------------------+
> +   | Field            | Value                  |
> +   +==================+========================+
> +   | ``mode``         | Quota operational mode |
> +   +------------------+------------------------+
> +   | ``quota``        | Quota value            |
> +   +------------------+------------------------+
> +
> +.. _rte_flow_quota_mode:
> +
> +.. table:: Quota update modes
> +
> +   +---------------------------------+-------------------------------------+
> +   | Value                           | Description                         |
> +   +=================================+=====================================+
> +   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
> +   +---------------------------------+-------------------------------------+
> +   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
> +   +------------------+----------------------------------------------------+
> +   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
> +   +------------------+----------------------------------------------------+
> +
>   Negative types
>   ~~~~~~~~~~~~~~
>   
> diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
> index 5b98e18032..0756cca241 100644
> --- a/doc/guides/rel_notes/release_23_03.rst
> +++ b/doc/guides/rel_notes/release_23_03.rst
> @@ -77,6 +77,18 @@ New Features
>     - ``rte_flow_async_action_handle_query_update``
>   
>   

Just one empty line between section items

> +* **Added quota flow action and quota flow item.**
> +
> +  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
> +  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
> +
> +
Just one empty line between section items

> +* **Updated testpmd to support quota flow action and item.**
> +
> +  Added support for flow quota action and item.
> +
> +
> +

It should be 2 empty lines, not 3,

>   Removed Items
>   -------------
>   

[snip]

> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index 6705cb32af..46409ed607 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -624,7 +624,45 @@ enum rte_flow_item_type {
>   	 * See struct rte_flow_item_meter_color.
>   	 */
>   	RTE_FLOW_ITEM_TYPE_METER_COLOR,
> +
> +	/**
> +	 * Match Quota state
> +	 *
> +	 * @see struct rte_flow_item_quota
> +	 */
> +	 RTE_FLOW_ITEM_TYPE_QUOTA,
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * QUOTA state.
> + *
> + * @see struct rte_flow_item_quota
> + */
> +enum rte_flow_quota_state {
> +	RTE_FLOW_QUOTA_STATE_PASS, /**< PASS quota state */
> +	RTE_FLOW_QUOTA_STATE_BLOCK /**< BLOCK quota state */
> +};
> +
> +/**
> + * RTE_FLOW_ITEM_TYPE_QUOTA
> + *
> + * Matches QUOTA state
> + */
> +struct rte_flow_item_quota {
> +	enum rte_flow_quota_state state;
> +};
> +
> +/**
> + * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
> + */
> +#ifndef __cplusplus
> +static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
> +	.state = (enum rte_flow_quota_state)0xff
>   };
> +#endif
>   
>   /**
>    *
> @@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
>   	 * No associated configuration structure.
>   	 */
>   	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
> +
> +	/**
> +	 * Apply the quota verdict (PASS or BLOCK) to a flow.
> +	 *
> +	 * @see struct rte_flow_action_quota
> +	 * @see struct rte_flow_query_quota
> +	 * @see struct rte_flow_update_quota
> +	 */
> +	 RTE_FLOW_ACTION_TYPE_QUOTA,
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * QUOTA operational mode.
> + *
> + * @see struct rte_flow_action_quota
> + */
> +enum rte_flow_quota_mode {
> +	RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */

/**<  since you put documentation after documented member

> +	RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting from L2 */

/**<

also, why do you initialize every member? are you going to rely
on these values?

> +	RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting from L3 */

/**<

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Create QUOTA action.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + */
> +struct rte_flow_action_quota {
> +	enum rte_flow_quota_mode mode; /** quota operational mode */

/**<  since you put documentation after documented member

> +	int64_t quota;                 /** quota value */

/**<

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query indirect QUOTA action.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + *
> + */
> +struct rte_flow_query_quota {
> +	int64_t quota; /** quota value */

/**<  since you put documentation after documented member

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Indirect QUOTA update operations.
> + *
> + * @see struct rte_flow_update_quota
> + */
> +enum rte_flow_update_quota_op {
> +	RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */

/**<  since you put documentation after documented member

> +	RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with new value */

/**<

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> + *
> + * Update indirect QUOTA action.
> + */
> +struct rte_flow_update_quota {
> +	enum rte_flow_update_quota_op op; /** update operation */

/**<  since you put documentation after documented member

> +	int64_t quota;                    /** quota value */

/**<

>   };
>   
>   /**
> @@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
>   	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
>   	 */
>   	uint32_t max_nb_conn_tracks;
> +	/**
> +	 * Maximum number of quota actions.
> +	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
> +	 */
> +	uint32_t max_nb_quotas;
>   	/**
>   	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
>   	 */
> @@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
>   	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
>   	 */
>   	uint32_t nb_conn_tracks;
> +	/**
> +	 * Maximum number of quota actions.
> +	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
> +	 */
> +	uint32_t nb_quotas;
>   	/**
>   	 * Port flags (RTE_FLOW_PORT_FLAG_*).
>   	 */


^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v6 1/2] ethdev: add query_update sync and async function calls
  2023-02-01 11:00           ` Andrew Rybchenko
@ 2023-02-01 14:03             ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-01 14:03 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

Hello Andrew,

> >
> >> On 1/20/23 13:46, Gregory Etelson wrote:
> >>>>> and it's async version `rte_flow_async_action_handle_query_update`
> >>>>> to atomically query and update flow action.
> >>>>
> >>>> Sorry, may be I'm missing something, but after reading previous
> >>>> discussion I have a feeling that update could be queried data
> >>>> dependent. However, I don't understand how it could be possible
> >>>> with API below. Just my misunderstanding?
> >>>>
> >>>
> >>> The goal of `rte_flow_action_handle_query_update()` is to execute
> query
> >> and update actions atomically.
> >>> The function guaranties that action state will not change by any event
> >> before both update and query complete.
> >>> If the function was called with the ` RTE_FLOW_QU_QUERY_FIRST `
> >> `mode` argument, then update execution can depend
> >>> on query result. That's up to query format, PMD implementation and
> >> hardware capabilities.
> >>> I hope that answered your question.
> >>
> >> Sorry, I'm still confused. Could you explain a bit more,
> >> please. How update could depend on query result?
> >
> > Conditional update I described requires special action properties.
> > Consider an action object with a method that receives query and update
> as parameters.
> > The method will activate update only if query result satisfies action state.
> > If the action was updated, both query and update were atomic for
> application.
> > The function returns the action state - updated or not.
> > Application is aware about the action properties.
> > Application applies action properties to returned state to discover if
> action was updated.
> 
> Is it just a theoretical possibility or do you have an example
> right now?
> 

Quota flow action is updated according to these rules.
You validate it with MLX5 hardware and patches from this batch.

Regards,
Gregory

^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v7 1/2] ethdev: add query_update sync and async function calls
  2023-02-01 11:16   ` [PATCH v7 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
@ 2023-02-01 14:52     ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-01 14:52 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

Hello Andrew,

[]

> > diff --git a/doc/guides/rel_notes/release_23_03.rst
> b/doc/guides/rel_notes/release_23_03.rst
> > index c15f6fbb9f..5b98e18032 100644
> > --- a/doc/guides/rel_notes/release_23_03.rst
> > +++ b/doc/guides/rel_notes/release_23_03.rst
> > @@ -68,6 +68,13 @@ New Features
> >     * With this change, the application no longer needs to account for the
> >       ``rte_event_dev_config::nb_single_link_event_port_queues``
> parameter
> >       required for eth_rx, eth_tx, crypto and timer eventdev adapters.
> 
> empty line is required here
>

Will fix in v8.

[]
 
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query and update operational mode.
> > + *
> > + * RTE_FLOW_QU_QUERY_FIRST
> > + *   Force port to query action before update.
> > + * RTE_FLOW_QU_UPDATE_FIRST
> > + *   Force port to update action before query.
> 
> Do not duplicate enum members.
> 

There is no duplication - 
query_first
update_first

> > + *
> > + * @see rte_flow_action_handle_query_update()
> > + * @see rte_flow_async_action_handle_query_update()
> > + */
> > +enum rte_flow_query_update_mode {
> > +     RTE_FLOW_QU_QUERY_FIRST,  /* query before update */
> 
> /**< to document enum member
> 
> /**< Force port to query action before update. */
> 
> if it fits line length or before the enum member:
> 
> /** Force port to query action before update. */
> 

Will fix in v8.

> > +     RTE_FLOW_QU_UPDATE_FIRST, /* query after  update */
> 
> same
> 

Will fix in v8.

> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query and/or update indirect flow action.
> > + * If both query and update not NULL, the function atomically
> > + * queries and updates indirect action. Query and update are carried in
> order
> > + * specified in the mode parameter.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param update
> > + *   If not NULL, update profile specification used to modify the action
> > + *   pointed by handle.
> > + * @param query
> > + *   If not NULL pointer to storage for the associated query data type.
> > + * @param mode
> > + *   Operational mode.
> > + *   Required if both *update* and *query* are not NULL.
> > + * @param 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.
> > + * - (-ENODEV) if *port_id* invalid.
> > + * - (-ENOTSUP) if underlying device does not support this functionality.
> > + * - (-EINVAL) - if *handle* invalid or both *query* and *update* are
> NULL.
> 
> Be consistent. You have no '-' before if in two cases above and
> have in the EINVAL case.
> 

Will fix in v8.

> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_action_handle_query_update(uint16_t port_id,
> > +                                 struct rte_flow_action_handle *handle,
> > +                                 const void *update, void *query,
> > +                                 enum rte_flow_query_update_mode mode,
> > +                                 struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue async indirect flow action query and/or update
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue which is used to update the rule.
> > + * @param attr
> > + *   Indirect action update operation attributes.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param update
> > + *   If not NULL, update profile specification used to modify the action
> > + *   pointed by handle.
> > + * @param query
> > + *   If not NULL, pointer to storage for the associated query data type.
> > + *   Query result returned on async completion event.
> > + * @param mode
> > + *   Operational mode.
> > + *   Required if both *update* and *query* are not NULL.
> > + * @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 on success, a negative errno value otherwise and rte_errno is set.
> > + * - (-ENODEV) if *port_id* invalid.
> > + * - (-ENOTSUP) if underlying device does not support this functionality.
> > + * - (-EINVAL) - if *handle* invalid or both *update* and *query* are
> NULL.
> 
> same
> 

Will fix in v8.

> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> > +                                       const struct rte_flow_op_attr *attr,
> > +                                       struct rte_flow_action_handle *handle,
> > +                                       const void *update, void *query,
> > +                                       enum rte_flow_query_update_mode mode,
> > +                                       void *user_data,
> > +                                       struct rte_flow_error *error);
> > +
> >   #ifdef __cplusplus
> >   }
> >   #endif
> 
> [snip]


^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v7 2/2] ethdev: add quota flow action and item
  2023-02-01 11:22     ` Andrew Rybchenko
@ 2023-02-01 15:09       ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-01 15:09 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, NBU-Contact-Thomas Monjalon (EXTERNAL)

Hello Andrew,

[]

> On 1/24/23 12:37, Gregory Etelson wrote:
> > Quota action limits traffic according to pre-defined configuration.
> > Quota reflects overall traffic usage regardless bandwidth.
> > Quota flow action initialized with signed tokens number value.
> > Quota flow action updates tokens number according to
> > these rules:
> > 1. if quota was configured to count packet length, for each packet
> >     of size S, tokens number reduced by S.
> > 2. If quota was configured to count packets, each packet decrements
> >     tokens number.
> > quota action sets packet metadata according to a number of remaining
> > tokens number:
> >    PASS - remaining tokens number is non-negative.
> >    BLOCK - remaining tokens number is negative.
> >
> > Quota flow item matches on that data
> >
> > Application updates tokens number in quota flow action
> > with SET or ADD calls:
> >   SET(QUOTA, val) - arm quota with new tokens number set to val
> >   ADD(QUOTA, val) - increase existing quota tokens number by val
> >
> > Both SET and ADD return to application number of tokens stored in port
> > before update.
> >
> > If quota state was BLOCK (negative action tokens number)
> > application can change it to PASS after providing enough tokens to
> > raise action tokens number to 0 or above.
> >
> > Application must create a rule with quota action to mark flow and
> > match on the mark with quota item in following flow rule.
> >
> > Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> > Acked-by: Ori Kam <orika@nvidia.com>
> 
> [snip]
> 
> > diff --git a/doc/guides/prog_guide/rte_flow.rst
> b/doc/guides/prog_guide/rte_flow.rst
> > index 3e6242803d..7a3868638c 100644
> > --- a/doc/guides/prog_guide/rte_flow.rst
> > +++ b/doc/guides/prog_guide/rte_flow.rst
> > @@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
> >
> >   - ``color``: Metering color marker.
> >
> > +Item: ``QUOTA``
> > +^^^^^^^^^^^^^^^
> > +
> > +Matches flow quota state set by quota action.
> > +
> > +- ``state``: Flow quota state
> > +
> >   Actions
> >   ~~~~~~~
> >
> > @@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
> >      | ``policy``       | Meter policy object  |
> >      +------------------+----------------------+
> >
> > +Action: ``QUOTA``
> > +^^^^^^^^^^^^^^^^^
> > +
> > +Update ``quota`` value and set packet quota state.
> > +If the ``quota`` value after update is non-negative packet quota state set
> to
> > +``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to
> ``RTE_FLOW_QUOTA_STATE_BLOCK``.
> > +The ``quota`` value is reduced according to ``mode`` setting.
> > +
> > +.. _table_rte_flow_action_quota:
> > +
> > +.. table:: QUOTA
> > +
> > +   +------------------+------------------------+
> > +   | Field            | Value                  |
> > +   +==================+========================+
> > +   | ``mode``         | Quota operational mode |
> > +   +------------------+------------------------+
> > +   | ``quota``        | Quota value            |
> > +   +------------------+------------------------+
> > +
> > +.. _rte_flow_quota_mode:
> > +
> > +.. table:: Quota update modes
> > +
> > +   +---------------------------------+-------------------------------------+
> > +   | Value                           | Description                         |
> > +
> +=================================+============================
> =========+
> > +   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets
> |
> > +   +---------------------------------+-------------------------------------+
> > +   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting
> from L2 |
> > +   +------------------+----------------------------------------------------+
> > +   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting
> from L3 |
> > +   +------------------+----------------------------------------------------+
> > +
> >   Negative types
> >   ~~~~~~~~~~~~~~
> >
> > diff --git a/doc/guides/rel_notes/release_23_03.rst
> b/doc/guides/rel_notes/release_23_03.rst
> > index 5b98e18032..0756cca241 100644
> > --- a/doc/guides/rel_notes/release_23_03.rst
> > +++ b/doc/guides/rel_notes/release_23_03.rst
> > @@ -77,6 +77,18 @@ New Features
> >     - ``rte_flow_async_action_handle_query_update``
> >
> >
> 
> Just one empty line between section items
> 

Will fix in v8.

> > +* **Added quota flow action and quota flow item.**
> > +
> > +  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
> > +  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
> > +
> > +
> Just one empty line between section items
> 

Will fix in v8.

> > +* **Updated testpmd to support quota flow action and item.**
> > +
> > +  Added support for flow quota action and item.
> > +
> > +
> > +
> 
> It should be 2 empty lines, not 3,
>

Will fix in v8.

 
> >   Removed Items
> >   -------------
> >
> 
> [snip]
> 
> > diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> > index 6705cb32af..46409ed607 100644
> > --- a/lib/ethdev/rte_flow.h
> > +++ b/lib/ethdev/rte_flow.h
> > @@ -624,7 +624,45 @@ enum rte_flow_item_type {
> >        * See struct rte_flow_item_meter_color.
> >        */
> >       RTE_FLOW_ITEM_TYPE_METER_COLOR,
> > +
> > +     /**
> > +      * Match Quota state
> > +      *
> > +      * @see struct rte_flow_item_quota
> > +      */
> > +      RTE_FLOW_ITEM_TYPE_QUOTA,
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * QUOTA state.
> > + *
> > + * @see struct rte_flow_item_quota
> > + */
> > +enum rte_flow_quota_state {
> > +     RTE_FLOW_QUOTA_STATE_PASS, /**< PASS quota state */
> > +     RTE_FLOW_QUOTA_STATE_BLOCK /**< BLOCK quota state */
> > +};
> > +
> > +/**
> > + * RTE_FLOW_ITEM_TYPE_QUOTA
> > + *
> > + * Matches QUOTA state
> > + */
> > +struct rte_flow_item_quota {
> > +     enum rte_flow_quota_state state;
> > +};
> > +
> > +/**
> > + * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
> > + */
> > +#ifndef __cplusplus
> > +static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
> > +     .state = (enum rte_flow_quota_state)0xff
> >   };
> > +#endif
> >
> >   /**
> >    *
> > @@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
> >        * No associated configuration structure.
> >        */
> >       RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
> > +
> > +     /**
> > +      * Apply the quota verdict (PASS or BLOCK) to a flow.
> > +      *
> > +      * @see struct rte_flow_action_quota
> > +      * @see struct rte_flow_query_quota
> > +      * @see struct rte_flow_update_quota
> > +      */
> > +      RTE_FLOW_ACTION_TYPE_QUOTA,
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * QUOTA operational mode.
> > + *
> > + * @see struct rte_flow_action_quota
> > + */
> > +enum rte_flow_quota_mode {
> > +     RTE_FLOW_QUOTA_MODE_PACKET = 1, /** Count packets */
> 
> /**<  since you put documentation after documented member
> 
> > +     RTE_FLOW_QUOTA_MODE_L2 = 2, /** Count packet bytes starting
> from L2 */
> 
> /**<
>

Will fix in v8.
 
> also, why do you initialize every member? are you going to rely
> on these values?
> 

I selected these values to avoid confusion.
L2 mode corresponds to numerical value 2 and
L3 mode corresponds to numerical value 3.

> > +     RTE_FLOW_QUOTA_MODE_L3 = 3, /** Count packet bytes starting
> from L3 */
> 
> /**<
> 
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Create QUOTA action.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + */
> > +struct rte_flow_action_quota {
> > +     enum rte_flow_quota_mode mode; /** quota operational mode */
> 
> /**<  since you put documentation after documented member
> 
> > +     int64_t quota;                 /** quota value */
> 
> /**<
> 

Will fix in v8.

> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query indirect QUOTA action.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + *
> > + */
> > +struct rte_flow_query_quota {
> > +     int64_t quota; /** quota value */
> 
> /**<  since you put documentation after documented member
>

Will fix in v8.
 
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Indirect QUOTA update operations.
> > + *
> > + * @see struct rte_flow_update_quota
> > + */
> > +enum rte_flow_update_quota_op {
> > +     RTE_FLOW_UPDATE_QUOTA_SET, /** set new quota value */
> 
> /**<  since you put documentation after documented member
> 
> > +     RTE_FLOW_UPDATE_QUOTA_ADD, /** increase existing quota with
> new value */
> 
> /**<
> 

Will fix in v8.

> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > + *
> > + * Update indirect QUOTA action.
> > + */
> > +struct rte_flow_update_quota {
> > +     enum rte_flow_update_quota_op op; /** update operation */
> 
> /**<  since you put documentation after documented member
> 
> > +     int64_t quota;                    /** quota value */
> 
> /**<
>

Will fix in v8.
 
> >   };
> >
> >   /**
> > @@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
> >        * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
> >        */
> >       uint32_t max_nb_conn_tracks;
> > +     /**
> > +      * Maximum number of quota actions.
> > +      * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > +      */
> > +     uint32_t max_nb_quotas;
> >       /**
> >        * Port supported flags (RTE_FLOW_PORT_FLAG_*).
> >        */
> > @@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
> >        * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
> >        */
> >       uint32_t nb_conn_tracks;
> > +     /**
> > +      * Maximum number of quota actions.
> > +      * @see RTE_FLOW_ACTION_TYPE_QUOTA
> > +      */
> > +     uint32_t nb_quotas;
> >       /**
> >        * Port flags (RTE_FLOW_PORT_FLAG_*).
> >        */


^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v8 1/2] ethdev: add query_update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (7 preceding siblings ...)
  2023-01-24  9:37 ` [PATCH v7 " Gregory Etelson
@ 2023-02-01 15:16 ` Gregory Etelson
  2023-02-01 15:16   ` [PATCH v8 2/2] ethdev: add quota flow action and item Gregory Etelson
                     ` (2 more replies)
  2023-02-02 11:54 ` [PATCH v9 1/2] ethdev: add query and update " Gregory Etelson
  2023-02-02 13:47 ` [PATCH v10 " Gregory Etelson
  10 siblings, 3 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-01 15:16 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If indirect action must be conditionally updated according to it's
present state application must first issue action query then
analyze returned data and if needed issue update request.
When the update will be processed, action state can change and
the update can invalidate the action.

Add `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

Application can control query and update order, if that is supported
by port hardware, by setting `qu_mode` parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release release notes.
    Fix doxygen errors.
v4: Add returned errno codes.
v5: Update the patch description.
    Fix typos.
v6: Resolve merge conflict with the main branch.	
v7: Update documentation.
v8: Style fixes.
---
 doc/guides/rel_notes/release_23_03.rst |   8 ++
 lib/ethdev/rte_flow.c                  |  45 +++++++++++
 lib/ethdev/rte_flow.h                  | 103 +++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  15 ++++
 lib/ethdev/version.map                 |   5 ++
 5 files changed, 176 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index c15f6fbb9f..8115f840fe 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -69,6 +69,14 @@ New Features
     ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
     required for eth_rx, eth_tx, crypto and timer eventdev adapters.
 
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..ece40e7877 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,48 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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);
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update, query,
+					      mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  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);
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update, query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..d3fbba5b99 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,109 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and update operational mode.
+ *
+ * RTE_FLOW_QU_QUERY_FIRST
+ *   Force port to query action before update.
+ * RTE_FLOW_QU_UPDATE_FIRST
+ *   Force port to update action before query.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /**< Query before update. */
+	RTE_FLOW_QU_UPDATE_FIRST, /**< Query after  update. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update are carried in order
+ * specified in the mode parameter.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ *   Required if both *update* and *query* are not NULL.
+ * @param 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.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* invalid or both *query* and *update* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL, pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ *   Required if both *update* and *query* are not NULL.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* invalid or both *update* and *query* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..628c486081 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# added in 23.03
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v8 2/2] ethdev: add quota flow action and item
  2023-02-01 15:16 ` [PATCH v8 " Gregory Etelson
@ 2023-02-01 15:16   ` Gregory Etelson
  2023-02-02  9:17     ` Andrew Rybchenko
  2023-02-01 17:30   ` [PATCH v8 1/2] ethdev: add query_update sync and async function calls Ori Kam
  2023-02-02  9:15   ` Andrew Rybchenko
  2 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-02-01 15:16 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Thomas Monjalon, Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

If quota state was BLOCK (negative action tokens number)
application can change it to PASS after providing enough tokens to
raise action tokens number to 0 or above.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release notes.
v5: Update the patch description.
    Remove PMD documentation.
v7: Update documentation.
v8: Style fixes.
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |   9 +
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 8 files changed, 654 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 8115f840fe..9aa3c6e935 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -77,6 +77,15 @@ New Features
   - ``rte_flow_action_handle_query_update``
   - ``rte_flow_async_action_handle_query_update``
 
+* **Added quota flow action and quota flow item.**
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added support for flow quota action and item.
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index ece40e7877..448cf1607e 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index d3fbba5b99..49c5418422 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /**< PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /**< BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply the quota verdict (PASS or BLOCK) to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /**< Count packets. */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /**< Count packet bytes starting from L2. */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /**< Count packet bytes starting from L3. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /**< Quota operational mode. */
+	int64_t quota;                 /**< Quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /**< Quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /**< Set new quota value. */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /**< Increase quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /**< Update operation. */
+	int64_t quota;                    /**< Quota value. */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* RE: [PATCH v8 1/2] ethdev: add query_update sync and async function calls
  2023-02-01 15:16 ` [PATCH v8 " Gregory Etelson
  2023-02-01 15:16   ` [PATCH v8 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-02-01 17:30   ` Ori Kam
  2023-02-02  9:15   ` Andrew Rybchenko
  2 siblings, 0 replies; 58+ messages in thread
From: Ori Kam @ 2023-02-01 17:30 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: Matan Azrad, Raslan Darawsheh,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit, Andrew Rybchenko

Hi Gregory,.


> -----Original Message-----
> From: Gregory Etelson <getelson@nvidia.com>
> Sent: Wednesday, 1 February 2023 17:17
> To: dev@dpdk.org
> Cc: Gregory Etelson <getelson@nvidia.com>; Matan Azrad
> <matan@nvidia.com>; Raslan Darawsheh <rasland@nvidia.com>; Ori Kam
> <orika@nvidia.com>; NBU-Contact-Thomas Monjalon (EXTERNAL)
> <thomas@monjalon.net>; Ferruh Yigit <ferruh.yigit@amd.com>; Andrew
> Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Subject: [PATCH v8 1/2] ethdev: add query_update sync and async function
> calls
> 
> Current API allows either query or update indirect flow action.
> If indirect action must be conditionally updated according to it's
> present state application must first issue action query then
> analyze returned data and if needed issue update request.
> When the update will be processed, action state can change and
> the update can invalidate the action.
> 
> Add `rte_flow_action_handle_query_update` function call,
> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.
> 
> Application can control query and update order, if that is supported
> by port hardware, by setting `qu_mode` parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> ---
> v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> v3: Update release release notes.
>     Fix doxygen errors.
> v4: Add returned errno codes.
> v5: Update the patch description.
>     Fix typos.
> v6: Resolve merge conflict with the main branch.
> v7: Update documentation.
> v8: Style fixes.
> ---

Acked-by: Ori Kam <orika@nvidia.com>
Best,
Ori

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v8 1/2] ethdev: add query_update sync and async function calls
  2023-02-01 15:16 ` [PATCH v8 " Gregory Etelson
  2023-02-01 15:16   ` [PATCH v8 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-02-01 17:30   ` [PATCH v8 1/2] ethdev: add query_update sync and async function calls Ori Kam
@ 2023-02-02  9:15   ` Andrew Rybchenko
  2023-02-02 10:24     ` Gregory Etelson
  2 siblings, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-02  9:15 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit

query_update is bad for summary, consider something like
ethdev: add query and update sync and async API

On 2/1/23 18:16, Gregory Etelson wrote:
> Current API allows either query or update indirect flow action.
> If indirect action must be conditionally updated according to it's
> present state application must first issue action query then
> analyze returned data and if needed issue update request.
> When the update will be processed, action state can change and
> the update can invalidate the action.
> 
> Add `rte_flow_action_handle_query_update` function call,
> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.
> 
> Application can control query and update order, if that is supported
> by port hardware, by setting `qu_mode` parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>

[snip]

> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> index 7d0c24366c..ece40e7877 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -1883,3 +1883,48 @@ rte_flow_async_action_handle_query(uint16_t port_id,
>   					  action_handle, data, user_data, error);
>   	return flow_err(port_id, ret, error);
>   }
> +
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode mode,
> +				    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);
> +	dev = &rte_eth_devices[port_id];

(-EINVAL) if *handle* invalid or both *query* and *update* are NULL.

must be handled here. It is generic checks and we should do it
in a generic place to avoid duplication in PMD callbacks.

> +	ops = rte_flow_ops_get(port_id, error);
> +	if (!ops || !ops->action_handle_query_update)
> +		return -ENOTSUP;

May be it makes sense to use single-operation callbacks if
another operation input is NULL. I guess it could be
convenient in some cases. Just an idea.

> +	ret = ops->action_handle_query_update(dev, handle, update, query,
> +					      mode, error);
> +	return flow_err(port_id, ret, error);
> +}
> +
> +int
> +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle *handle,
> +					  const void *update, void *query,
> +					  enum rte_flow_query_update_mode mode,
> +					  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);
> +	dev = &rte_eth_devices[port_id];

same here

> +	ops = rte_flow_ops_get(port_id, error);
> +	if (!ops || !ops->async_action_handle_query_update)
> +		return -ENOTSUP;

same here

> +	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
> +						    handle, update, query, mode,
> +						    user_data, error);
> +	return flow_err(port_id, ret, error);
> +}
> diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> index b60987db4b..d3fbba5b99 100644
> --- a/lib/ethdev/rte_flow.h
> +++ b/lib/ethdev/rte_flow.h
> @@ -5622,6 +5622,109 @@ rte_flow_async_action_handle_query(uint16_t port_id,
>   		void *user_data,
>   		struct rte_flow_error *error);
>   
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and update operational mode.
> + *
> + * RTE_FLOW_QU_QUERY_FIRST
> + *   Force port to query action before update.
> + * RTE_FLOW_QU_UPDATE_FIRST
> + *   Force port to update action before query.

I'm sorry, but I strongly dislike enum members duplication
here. I don't understand why we need to duplicate it and why
we can't document it in a right way below.

> + *
> + * @see rte_flow_action_handle_query_update()
> + * @see rte_flow_async_action_handle_query_update()
> + */
> +enum rte_flow_query_update_mode {
> +	RTE_FLOW_QU_QUERY_FIRST,  /**< Query before update. */
> +	RTE_FLOW_QU_UPDATE_FIRST, /**< Query after  update. */
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Query and/or update indirect flow action.
> + * If both query and update not NULL, the function atomically
> + * queries and updates indirect action. Query and update are carried in order
> + * specified in the mode parameter.
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param update
> + *   If not NULL, update profile specification used to modify the action
> + *   pointed by handle.
> + * @param query
> + *   If not NULL pointer to storage for the associated query data type.
> + * @param mode
> + *   Operational mode.
> + *   Required if both *update* and *query* are not NULL.

It should be a part of generic checks in
rte_flow_action_handle_query_update() implementation.

> + * @param 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.
> + * - (-ENODEV) if *port_id* invalid.
> + * - (-ENOTSUP) if underlying device does not support this functionality.
> + * - (-EINVAL) if *handle* invalid or both *query* and *update* are NULL.
> + */
> +__rte_experimental
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode mode,
> +				    struct rte_flow_error *error);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Enqueue async indirect flow action query and/or update
> + *
> + * @param port_id
> + *   Port identifier of Ethernet device.
> + * @param queue_id
> + *   Flow queue which is used to update the rule.
> + * @param attr
> + *   Indirect action update operation attributes.
> + * @param handle
> + *   Handle for the indirect action object to be updated.
> + * @param update
> + *   If not NULL, update profile specification used to modify the action
> + *   pointed by handle.
> + * @param query
> + *   If not NULL, pointer to storage for the associated query data type.
> + *   Query result returned on async completion event.
> + * @param mode
> + *   Operational mode.
> + *   Required if both *update* and *query* are not NULL.

It should be a part of generic checks in
rte_flow_action_handle_query_update() implementation.

> + * @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 on success, a negative errno value otherwise and rte_errno is set.
> + * - (-ENODEV) if *port_id* invalid.
> + * - (-ENOTSUP) if underlying device does not support this functionality.
> + * - (-EINVAL) if *handle* invalid or both *update* and *query* are NULL.
> + */
> +__rte_experimental
> +int
> +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
> +					  const struct rte_flow_op_attr *attr,
> +					  struct rte_flow_action_handle *handle,
> +					  const void *update, void *query,
> +					  enum rte_flow_query_update_mode mode,
> +					  void *user_data,
> +					  struct rte_flow_error *error);
> +
>   #ifdef __cplusplus
>   }
>   #endif

[snip]



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v8 2/2] ethdev: add quota flow action and item
  2023-02-01 15:16   ` [PATCH v8 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-02-02  9:17     ` Andrew Rybchenko
  0 siblings, 0 replies; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-02  9:17 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Aman Singh, Yuying Zhang, Ferruh Yigit,
	Thomas Monjalon

On 2/1/23 18:16, Gregory Etelson wrote:
> Quota action limits traffic according to pre-defined configuration.
> Quota reflects overall traffic usage regardless bandwidth.
> Quota flow action initialized with signed tokens number value.
> Quota flow action updates tokens number according to
> these rules:
> 1. if quota was configured to count packet length, for each packet
>     of size S, tokens number reduced by S.
> 2. If quota was configured to count packets, each packet decrements
>     tokens number.
> quota action sets packet metadata according to a number of remaining
> tokens number:
>    PASS - remaining tokens number is non-negative.
>    BLOCK - remaining tokens number is negative.
> 
> Quota flow item matches on that data
> 
> Application updates tokens number in quota flow action
> with SET or ADD calls:
>   SET(QUOTA, val) - arm quota with new tokens number set to val
>   ADD(QUOTA, val) - increase existing quota tokens number by val
> 
> Both SET and ADD return to application number of tokens stored in port
> before update.
> 
> If quota state was BLOCK (negative action tokens number)
> application can change it to PASS after providing enough tokens to
> raise action tokens number to 0 or above.
> 
> Application must create a rule with quota action to mark flow and
> match on the mark with quota item in following flow rule.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>

Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>



^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v8 1/2] ethdev: add query_update sync and async function calls
  2023-02-02  9:15   ` Andrew Rybchenko
@ 2023-02-02 10:24     ` Gregory Etelson
  2023-02-02 10:30       ` Andrew Rybchenko
  0 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-02-02 10:24 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

Hello Andrew,

Please check out my comments below.

Regards,
Gregory

> 
> 
> query_update is bad for summary, consider something like
> ethdev: add query and update sync and async API
>

Will fix in v9.
 
> On 2/1/23 18:16, Gregory Etelson wrote:
> > Current API allows either query or update indirect flow action.
> > If indirect action must be conditionally updated according to it's
> > present state application must first issue action query then
> > analyze returned data and if needed issue update request.
> > When the update will be processed, action state can change and
> > the update can invalidate the action.
> >
> > Add `rte_flow_action_handle_query_update` function call,
> > and it's async version `rte_flow_async_action_handle_query_update`
> > to atomically query and update flow action.
> >
> > Application can control query and update order, if that is supported
> > by port hardware, by setting `qu_mode` parameter to
> > RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> >
> > Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> 
> [snip]
> 
> > diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> > index 7d0c24366c..ece40e7877 100644
> > --- a/lib/ethdev/rte_flow.c
> > +++ b/lib/ethdev/rte_flow.c
> > @@ -1883,3 +1883,48 @@ rte_flow_async_action_handle_query(uint16_t
> port_id,
> >                                         action_handle, data, user_data, error);
> >       return flow_err(port_id, ret, error);
> >   }
> > +
> > +int
> > +rte_flow_action_handle_query_update(uint16_t port_id,
> > +                                 struct rte_flow_action_handle *handle,
> > +                                 const void *update, void *query,
> > +                                 enum rte_flow_query_update_mode mode,
> > +                                 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);
> > +     dev = &rte_eth_devices[port_id];
> 
> (-EINVAL) if *handle* invalid or both *query* and *update* are NULL.
> 
> must be handled here. It is generic checks and we should do it
> in a generic place to avoid duplication in PMD callbacks.
> 

Will fix in v9.

> > +     ops = rte_flow_ops_get(port_id, error);
> > +     if (!ops || !ops->action_handle_query_update)
> > +             return -ENOTSUP;
> 
> May be it makes sense to use single-operation callbacks if
> another operation input is NULL. I guess it could be
> convenient in some cases. Just an idea.
> 

There is a plan to deprecate indirect query and update functions
and replace them with a single query_update.

> > +     ret = ops->action_handle_query_update(dev, handle, update, query,
> > +                                           mode, error);
> > +     return flow_err(port_id, ret, error);
> > +}
> > +
> > +int
> > +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> > +                                       const struct rte_flow_op_attr *attr,
> > +                                       struct rte_flow_action_handle *handle,
> > +                                       const void *update, void *query,
> > +                                       enum rte_flow_query_update_mode mode,
> > +                                       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);
> > +     dev = &rte_eth_devices[port_id];
> 
> same here
> 

Will fix in v9.

> > +     ops = rte_flow_ops_get(port_id, error);
> > +     if (!ops || !ops->async_action_handle_query_update)
> > +             return -ENOTSUP;
> 
> same here
>

Will fix in v9.
 
> > +     ret = ops->async_action_handle_query_update(dev, queue_id, attr,
> > +                                                 handle, update, query, mode,
> > +                                                 user_data, error);
> > +     return flow_err(port_id, ret, error);
> > +}
> > diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
> > index b60987db4b..d3fbba5b99 100644
> > --- a/lib/ethdev/rte_flow.h
> > +++ b/lib/ethdev/rte_flow.h
> > @@ -5622,6 +5622,109 @@
> rte_flow_async_action_handle_query(uint16_t port_id,
> >               void *user_data,
> >               struct rte_flow_error *error);
> >
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query and update operational mode.
> > + *
> > + * RTE_FLOW_QU_QUERY_FIRST
> > + *   Force port to query action before update.
> > + * RTE_FLOW_QU_UPDATE_FIRST
> > + *   Force port to update action before query.
> 
> I'm sorry, but I strongly dislike enum members duplication
> here. I don't understand why we need to duplicate it and why
> we can't document it in a right way below.
> 

I don't understand where the duplication is.
Query and update operations are atomic for application, 
but in hardware these are 2 separate operations that reference hardware object.
The operations execution order can have different results on object state. 
When application asks both actions it must explicitly specify execution order for hardware.

> > + *
> > + * @see rte_flow_action_handle_query_update()
> > + * @see rte_flow_async_action_handle_query_update()
> > + */
> > +enum rte_flow_query_update_mode {
> > +     RTE_FLOW_QU_QUERY_FIRST,  /**< Query before update. */
> > +     RTE_FLOW_QU_UPDATE_FIRST, /**< Query after  update. */
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Query and/or update indirect flow action.
> > + * If both query and update not NULL, the function atomically
> > + * queries and updates indirect action. Query and update are carried in
> order
> > + * specified in the mode parameter.
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param update
> > + *   If not NULL, update profile specification used to modify the action
> > + *   pointed by handle.
> > + * @param query
> > + *   If not NULL pointer to storage for the associated query data type.
> > + * @param mode
> > + *   Operational mode.
> > + *   Required if both *update* and *query* are not NULL.
> 
> It should be a part of generic checks in
> rte_flow_action_handle_query_update() implementation.
> 

Will fix in v9.

> > + * @param 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.
> > + * - (-ENODEV) if *port_id* invalid.
> > + * - (-ENOTSUP) if underlying device does not support this functionality.
> > + * - (-EINVAL) if *handle* invalid or both *query* and *update* are
> NULL.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_action_handle_query_update(uint16_t port_id,
> > +                                 struct rte_flow_action_handle *handle,
> > +                                 const void *update, void *query,
> > +                                 enum rte_flow_query_update_mode mode,
> > +                                 struct rte_flow_error *error);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice.
> > + *
> > + * Enqueue async indirect flow action query and/or update
> > + *
> > + * @param port_id
> > + *   Port identifier of Ethernet device.
> > + * @param queue_id
> > + *   Flow queue which is used to update the rule.
> > + * @param attr
> > + *   Indirect action update operation attributes.
> > + * @param handle
> > + *   Handle for the indirect action object to be updated.
> > + * @param update
> > + *   If not NULL, update profile specification used to modify the action
> > + *   pointed by handle.
> > + * @param query
> > + *   If not NULL, pointer to storage for the associated query data type.
> > + *   Query result returned on async completion event.
> > + * @param mode
> > + *   Operational mode.
> > + *   Required if both *update* and *query* are not NULL.
> 
> It should be a part of generic checks in
> rte_flow_action_handle_query_update() implementation.
>

Will fix in v9.
 
> > + * @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 on success, a negative errno value otherwise and rte_errno is set.
> > + * - (-ENODEV) if *port_id* invalid.
> > + * - (-ENOTSUP) if underlying device does not support this functionality.
> > + * - (-EINVAL) if *handle* invalid or both *update* and *query* are
> NULL.
> > + */
> > +__rte_experimental
> > +int
> > +rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t
> queue_id,
> > +                                       const struct rte_flow_op_attr *attr,
> > +                                       struct rte_flow_action_handle *handle,
> > +                                       const void *update, void *query,
> > +                                       enum rte_flow_query_update_mode mode,
> > +                                       void *user_data,
> > +                                       struct rte_flow_error *error);
> > +
> >   #ifdef __cplusplus
> >   }
> >   #endif
> 
> [snip]
> 


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v8 1/2] ethdev: add query_update sync and async function calls
  2023-02-02 10:24     ` Gregory Etelson
@ 2023-02-02 10:30       ` Andrew Rybchenko
  2023-02-02 10:44         ` Gregory Etelson
  0 siblings, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-02 10:30 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

On 2/2/23 13:24, Gregory Etelson wrote:
>> On 2/1/23 18:16, Gregory Etelson wrote:
>>> +     ops = rte_flow_ops_get(port_id, error);
>>> +     if (!ops || !ops->action_handle_query_update)
>>> +             return -ENOTSUP;
>>
>> May be it makes sense to use single-operation callbacks if
>> another operation input is NULL. I guess it could be
>> convenient in some cases. Just an idea.
>>
> 
> There is a plan to deprecate indirect query and update functions
> and replace them with a single query_update.

So, will you add corresponding code? Just want to clarify.

>>> +/**
>>> + * @warning
>>> + * @b EXPERIMENTAL: this API may change without prior notice.
>>> + *
>>> + * Query and update operational mode.
>>> + *
>>> + * RTE_FLOW_QU_QUERY_FIRST
>>> + *   Force port to query action before update.
>>> + * RTE_FLOW_QU_UPDATE_FIRST
>>> + *   Force port to update action before query.
>>
>> I'm sorry, but I strongly dislike enum members duplication
>> here. I don't understand why we need to duplicate it and why
>> we can't document it in a right way below.
>>
> 
> I don't understand where the duplication is.
> Query and update operations are atomic for application,
> but in hardware these are 2 separate operations that reference hardware object.
> The operations execution order can have different results on object state.
> When application asks both actions it must explicitly specify execution order for hardware.

No-no, simpler. Just remove RTE_FLOW_QU_QUERY_FIRST and
RTE_FLOW_QU_UPDATE_FIRST above and update comments below
to use comments from above. That's it.

>>> + *
>>> + * @see rte_flow_action_handle_query_update()
>>> + * @see rte_flow_async_action_handle_query_update()
>>> + */
>>> +enum rte_flow_query_update_mode {
>>> +     RTE_FLOW_QU_QUERY_FIRST,  /**< Query before update. */
>>> +     RTE_FLOW_QU_UPDATE_FIRST, /**< Query after  update. */
>>> +};


^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v8 1/2] ethdev: add query_update sync and async function calls
  2023-02-02 10:30       ` Andrew Rybchenko
@ 2023-02-02 10:44         ` Gregory Etelson
  2023-02-02 10:47           ` Andrew Rybchenko
  0 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-02-02 10:44 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

Hello Andrew,

[]

> On 2/2/23 13:24, Gregory Etelson wrote:
> >> On 2/1/23 18:16, Gregory Etelson wrote:
> >>> +     ops = rte_flow_ops_get(port_id, error);
> >>> +     if (!ops || !ops->action_handle_query_update)
> >>> +             return -ENOTSUP;
> >>
> >> May be it makes sense to use single-operation callbacks if
> >> another operation input is NULL. I guess it could be
> >> convenient in some cases. Just an idea.
> >>
> >
> > There is a plan to deprecate indirect query and update functions
> > and replace them with a single query_update.
> 
> So, will you add corresponding code? Just want to clarify.
>

There is ongoing work on next stage of indirect action functions.
The plan is to deprecate existing API when all updates will be introduced. 
 
> >>> +/**
> >>> + * @warning
> >>> + * @b EXPERIMENTAL: this API may change without prior notice.
> >>> + *
> >>> + * Query and update operational mode.
> >>> + *
> >>> + * RTE_FLOW_QU_QUERY_FIRST
> >>> + *   Force port to query action before update.
> >>> + * RTE_FLOW_QU_UPDATE_FIRST
> >>> + *   Force port to update action before query.
> >>
> >> I'm sorry, but I strongly dislike enum members duplication
> >> here. I don't understand why we need to duplicate it and why
> >> we can't document it in a right way below.
> >>
> >
> > I don't understand where the duplication is.
> > Query and update operations are atomic for application,
> > but in hardware these are 2 separate operations that reference hardware
> object.
> > The operations execution order can have different results on object state.
> > When application asks both actions it must explicitly specify execution
> order for hardware.
> 
> No-no, simpler. Just remove RTE_FLOW_QU_QUERY_FIRST and
> RTE_FLOW_QU_UPDATE_FIRST above and update comments below
> to use comments from above. That's it.
> 

I see. Thank you !
Will fix in v9

> >>> + *
> >>> + * @see rte_flow_action_handle_query_update()
> >>> + * @see rte_flow_async_action_handle_query_update()
> >>> + */
> >>> +enum rte_flow_query_update_mode {
> >>> +     RTE_FLOW_QU_QUERY_FIRST,  /**< Query before update. */
> >>> +     RTE_FLOW_QU_UPDATE_FIRST, /**< Query after  update. */
> >>> +};


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v8 1/2] ethdev: add query_update sync and async function calls
  2023-02-02 10:44         ` Gregory Etelson
@ 2023-02-02 10:47           ` Andrew Rybchenko
  0 siblings, 0 replies; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-02 10:47 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

On 2/2/23 13:44, Gregory Etelson wrote:
> Hello Andrew,
> 
> []
> 
>> On 2/2/23 13:24, Gregory Etelson wrote:
>>>> On 2/1/23 18:16, Gregory Etelson wrote:
>>>>> +     ops = rte_flow_ops_get(port_id, error);
>>>>> +     if (!ops || !ops->action_handle_query_update)
>>>>> +             return -ENOTSUP;
>>>>
>>>> May be it makes sense to use single-operation callbacks if
>>>> another operation input is NULL. I guess it could be
>>>> convenient in some cases. Just an idea.
>>>>
>>>
>>> There is a plan to deprecate indirect query and update functions
>>> and replace them with a single query_update.
>>
>> So, will you add corresponding code? Just want to clarify.
>>
> 
> There is ongoing work on next stage of indirect action functions.
> The plan is to deprecate existing API when all updates will be introduced.

IMHO nothing prevents to add handling right now. It will allow
to use query_update instead of just query or update from the
very beginning. Not a strong requirement. Just nice to have
from my point of view.



^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v9 1/2] ethdev: add query and update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (8 preceding siblings ...)
  2023-02-01 15:16 ` [PATCH v8 " Gregory Etelson
@ 2023-02-02 11:54 ` Gregory Etelson
  2023-02-02 11:54   ` [PATCH v9 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-02-02 12:13   ` [PATCH v9 1/2] ethdev: add query and update sync and async function calls Andrew Rybchenko
  2023-02-02 13:47 ` [PATCH v10 " Gregory Etelson
  10 siblings, 2 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-02 11:54 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If indirect action must be conditionally updated according to it's
present state application must first issue action query then
analyze returned data and if needed issue update request.
When the update will be processed, action state can change and
the update can invalidate the action.

Add `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

Application can control query and update order, if that is supported
by port hardware, by setting `qu_mode` parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release release notes.
    Fix doxygen errors.
v4: Add returned errno codes.
v5: Update the patch description.
    Fix typos.
v6: Resolve merge conflict with the main branch.	
v7: Update documentation.
v8: Style fixes.
v9: Add parameters validation.
---
 doc/guides/rel_notes/release_23_03.rst |   8 ++
 lib/ethdev/rte_flow.c                  |  82 ++++++++++++++++++++
 lib/ethdev/rte_flow.h                  | 100 +++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  15 ++++
 lib/ethdev/version.map                 |   5 ++
 5 files changed, 210 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 73f5d94e14..5ab90f5118 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -78,6 +78,14 @@ New Features
     ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
     required for eth_rx, eth_tx, crypto and timer eventdev adapters.
 
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..4554c8f021 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,85 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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);
+	if (!handle)
+		return -EINVAL;
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops)
+		return -ENOTSUP;
+	if (update && query) {
+		if (!ops->action_handle_query_update)
+			return -ENOTSUP;
+		if (mode != RTE_FLOW_QU_QUERY_FIRST &&
+		    mode != RTE_FLOW_QU_UPDATE_FIRST)
+			return -EINVAL;
+		ret = ops->action_handle_query_update(dev, handle, update,
+						      query, mode, error);
+	} else if (!update && query) {
+		ret = rte_flow_action_handle_query(port_id, handle, query,
+						   error);
+	} else if (update && !query) {
+		ret = rte_flow_action_handle_update(port_id, handle, update,
+						    error);
+	} else {
+		return -EINVAL;
+	}
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  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);
+	if (!handle)
+		return -EINVAL;
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops)
+		return -ENOTSUP;
+	if (update && query) {
+		if (!ops->async_action_handle_query_update)
+			return -ENOTSUP;
+		if (mode != RTE_FLOW_QU_QUERY_FIRST &&
+		    mode != RTE_FLOW_QU_UPDATE_FIRST)
+			return -EINVAL;
+		ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+							    handle, update,
+							    query, mode,
+							    user_data, error);
+	} else if (!update && query) {
+		ret = rte_flow_async_action_handle_query(port_id, queue_id,
+							 attr, handle, query,
+							 user_data, error);
+	} else if (update && !query) {
+		ret = rte_flow_async_action_handle_update(port_id, queue_id,
+							  attr, handle, update,
+							  user_data, error);
+	} else {
+		return -EINVAL;
+	}
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..a10bba2792 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,106 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and update operational mode.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST,  /**< Query before update. */
+	RTE_FLOW_QU_UPDATE_FIRST, /**< Query after  update. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update are carried in order
+ * specified in the mode parameter.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ *   Required if both *update* and *query* are not NULL.
+ * @param 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.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* or *mode* invalid or
+ *             both *query* and *update* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL, pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ *   Required if both *update* and *query* are not NULL.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* or *mode* invalid or
+ *             both *update* and *query* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..628c486081 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# added in 23.03
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v9 2/2] ethdev: add quota flow action and item
  2023-02-02 11:54 ` [PATCH v9 1/2] ethdev: add query and update " Gregory Etelson
@ 2023-02-02 11:54   ` Gregory Etelson
  2023-02-02 12:13   ` [PATCH v9 1/2] ethdev: add query and update sync and async function calls Andrew Rybchenko
  1 sibling, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-02 11:54 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Thomas Monjalon, Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

If quota state was BLOCK (negative action tokens number)
application can change it to PASS after providing enough tokens to
raise action tokens number to 0 or above.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |   9 +
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 8 files changed, 654 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 5ab90f5118..8ffc056480 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -86,6 +86,15 @@ New Features
   - ``rte_flow_action_handle_query_update``
   - ``rte_flow_async_action_handle_query_update``
 
+* **Added quota flow action and quota flow item.**
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added support for flow quota action and item.
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 4554c8f021..c8343f1c40 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index a10bba2792..5d488a726a 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /**< PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /**< BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply the quota verdict (PASS or BLOCK) to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /**< Count packets. */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /**< Count packet bytes starting from L2. */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /**< Count packet bytes starting from L3. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /**< Quota operational mode. */
+	int64_t quota;                 /**< Quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /**< Quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /**< Set new quota value. */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /**< Increase quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /**< Update operation. */
+	int64_t quota;                    /**< Quota value. */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [PATCH v9 1/2] ethdev: add query and update sync and async function calls
  2023-02-02 11:54 ` [PATCH v9 1/2] ethdev: add query and update " Gregory Etelson
  2023-02-02 11:54   ` [PATCH v9 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-02-02 12:13   ` Andrew Rybchenko
  2023-02-09 19:16     ` Gregory Etelson
  1 sibling, 1 reply; 58+ messages in thread
From: Andrew Rybchenko @ 2023-02-02 12:13 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit

On 2/2/23 14:54, Gregory Etelson wrote:
> diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> index 7d0c24366c..4554c8f021 100644
> --- a/lib/ethdev/rte_flow.c
> +++ b/lib/ethdev/rte_flow.c
> @@ -1883,3 +1883,85 @@ rte_flow_async_action_handle_query(uint16_t port_id,
>   					  action_handle, data, user_data, error);
>   	return flow_err(port_id, ret, error);
>   }
> +
> +int
> +rte_flow_action_handle_query_update(uint16_t port_id,
> +				    struct rte_flow_action_handle *handle,
> +				    const void *update, void *query,
> +				    enum rte_flow_query_update_mode mode,
> +				    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);
> +	if (!handle)
> +		return -EINVAL;
> +	dev = &rte_eth_devices[port_id];
> +	ops = rte_flow_ops_get(port_id, error);
> +	if (!ops)
> +		return -ENOTSUP;
> +	if (update && query) {
> +		if (!ops->action_handle_query_update)
> +			return -ENOTSUP;
> +		if (mode != RTE_FLOW_QU_QUERY_FIRST &&
> +		    mode != RTE_FLOW_QU_UPDATE_FIRST)
> +			return -EINVAL;

Shouldn't it be checked in any case?
Also I'd initialize RTE_FLOW_QU_QUERY_FIRST to 1 on enum
definition to ensure that just 0 is not used as a mode value.
Also "Required if both *update* and *query* are not NULL." does
not make sense in the description since you have no way to skip
it.

> +		ret = ops->action_handle_query_update(dev, handle, update,
> +						      query, mode, error);
> +	} else if (!update && query) {
> +		ret = rte_flow_action_handle_query(port_id, handle, query,
> +						   error);
> +	} else if (update && !query) {
> +		ret = rte_flow_action_handle_update(port_id, handle, update,
> +						    error);
> +	} else {
> +		return -EINVAL;
> +	}

IMHO logic is wrong above since it should be sufficient to
implement just one action_handle_query_update callback instead
of all three.

I.e. if action_handle_query_update is available, it should be
used in any case.

> +	return flow_err(port_id, ret, error);
> +}
>   };
>   
>   INTERNAL {

same for async API


^ permalink raw reply	[flat|nested] 58+ messages in thread

* [PATCH v10 1/2] ethdev: add query and update sync and async function calls
  2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
                   ` (9 preceding siblings ...)
  2023-02-02 11:54 ` [PATCH v9 1/2] ethdev: add query and update " Gregory Etelson
@ 2023-02-02 13:47 ` Gregory Etelson
  2023-02-02 13:47   ` [PATCH v10 2/2] ethdev: add quota flow action and item Gregory Etelson
                     ` (3 more replies)
  10 siblings, 4 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-02 13:47 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Thomas Monjalon, Ferruh Yigit,
	Andrew Rybchenko

Current API allows either query or update indirect flow action.
If indirect action must be conditionally updated according to it's
present state application must first issue action query then
analyze returned data and if needed issue update request.
When the update will be processed, action state can change and
the update can invalidate the action.

Add `rte_flow_action_handle_query_update` function call,
and it's async version `rte_flow_async_action_handle_query_update`
to atomically query and update flow action.

Application can control query and update order, if that is supported
by port hardware, by setting `qu_mode` parameter to
RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
v3: Update release release notes.
    Fix doxygen errors.
v4: Add returned errno codes.
v5: Update the patch description.
    Fix typos.
v6: Resolve merge conflict with the main branch.	
v7: Update documentation.
v8: Style fixes.
v9: Add parameters validation.
v10: Code update.
---
 doc/guides/rel_notes/release_23_03.rst |   8 ++
 lib/ethdev/rte_flow.c                  |  54 +++++++++++++
 lib/ethdev/rte_flow.h                  | 100 +++++++++++++++++++++++++
 lib/ethdev/rte_flow_driver.h           |  15 ++++
 lib/ethdev/version.map                 |   5 ++
 5 files changed, 182 insertions(+)

diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 73f5d94e14..5ab90f5118 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -78,6 +78,14 @@ New Features
     ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
     required for eth_rx, eth_tx, crypto and timer eventdev adapters.
 
+* **Added functions to atomically query and update indirect flow action.**
+
+  Added synchronous and asynchronous functions to atomically query and update
+  indirect flow action:
+
+  - ``rte_flow_action_handle_query_update``
+  - ``rte_flow_async_action_handle_query_update``
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 7d0c24366c..923d795be1 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -1883,3 +1883,57 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 					  action_handle, data, user_data, error);
 	return flow_err(port_id, ret, error);
 }
+
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    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);
+	if (!handle)
+		return -EINVAL;
+	if (!update && !query)
+		return -EINVAL;
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->action_handle_query_update(dev, handle, update,
+					      query, mode, error);
+	return flow_err(port_id, ret, error);
+}
+
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  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);
+	if (!handle)
+		return -EINVAL;
+	if (!update && !query)
+		return -EINVAL;
+	dev = &rte_eth_devices[port_id];
+	ops = rte_flow_ops_get(port_id, error);
+	if (!ops || !ops->async_action_handle_query_update)
+		return -ENOTSUP;
+	ret = ops->async_action_handle_query_update(dev, queue_id, attr,
+						    handle, update,
+						    query, mode,
+						    user_data, error);
+	return flow_err(port_id, ret, error);
+}
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b60987db4b..b754330579 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -5622,6 +5622,106 @@ rte_flow_async_action_handle_query(uint16_t port_id,
 		void *user_data,
 		struct rte_flow_error *error);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and update operational mode.
+ *
+ * @see rte_flow_action_handle_query_update()
+ * @see rte_flow_async_action_handle_query_update()
+ */
+enum rte_flow_query_update_mode {
+	RTE_FLOW_QU_QUERY_FIRST = 1,  /**< Query before update. */
+	RTE_FLOW_QU_UPDATE_FIRST,     /**< Query after  update. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query and/or update indirect flow action.
+ * If both query and update not NULL, the function atomically
+ * queries and updates indirect action. Query and update are carried in order
+ * specified in the mode parameter.
+ * If ether query or update is NULL, the function executes
+ * complementing operation.
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL pointer to storage for the associated query data type.
+ * @param mode
+ *   Operational mode.
+ * @param 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.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* or *mode* invalid or
+ *             both *query* and *update* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_action_handle_query_update(uint16_t port_id,
+				    struct rte_flow_action_handle *handle,
+				    const void *update, void *query,
+				    enum rte_flow_query_update_mode mode,
+				    struct rte_flow_error *error);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Enqueue async indirect flow action query and/or update
+ *
+ * @param port_id
+ *   Port identifier of Ethernet device.
+ * @param queue_id
+ *   Flow queue which is used to update the rule.
+ * @param attr
+ *   Indirect action update operation attributes.
+ * @param handle
+ *   Handle for the indirect action object to be updated.
+ * @param update
+ *   If not NULL, update profile specification used to modify the action
+ *   pointed by handle.
+ * @param query
+ *   If not NULL, pointer to storage for the associated query data type.
+ *   Query result returned on async completion event.
+ * @param mode
+ *   Operational mode.
+ * @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 on success, a negative errno value otherwise and rte_errno is set.
+ * - (-ENODEV) if *port_id* invalid.
+ * - (-ENOTSUP) if underlying device does not support this functionality.
+ * - (-EINVAL) if *handle* or *mode* invalid or
+ *             both *update* and *query* are NULL.
+ */
+__rte_experimental
+int
+rte_flow_async_action_handle_query_update(uint16_t port_id, uint32_t queue_id,
+					  const struct rte_flow_op_attr *attr,
+					  struct rte_flow_action_handle *handle,
+					  const void *update, void *query,
+					  enum rte_flow_query_update_mode mode,
+					  void *user_data,
+					  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 c7d0699c91..7358c10a7a 100644
--- a/lib/ethdev/rte_flow_driver.h
+++ b/lib/ethdev/rte_flow_driver.h
@@ -114,6 +114,13 @@ struct rte_flow_ops {
 		 const struct rte_flow_action_handle *handle,
 		 void *data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_action_handle_query_update() */
+	int (*action_handle_query_update)
+		(struct rte_eth_dev *dev,
+		 struct rte_flow_action_handle *handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 struct rte_flow_error *error);
 	/** See rte_flow_tunnel_decap_set() */
 	int (*tunnel_decap_set)
 		(struct rte_eth_dev *dev,
@@ -276,6 +283,14 @@ struct rte_flow_ops {
 		 void *data,
 		 void *user_data,
 		 struct rte_flow_error *error);
+	/** See rte_flow_async_action_handle_query_update */
+	int (*async_action_handle_query_update)
+		(struct rte_eth_dev *dev, uint32_t queue_id,
+		 const struct rte_flow_op_attr *op_attr,
+		 struct rte_flow_action_handle *action_handle,
+		 const void *update, void *query,
+		 enum rte_flow_query_update_mode qu_mode,
+		 void *user_data, struct rte_flow_error *error);
 };
 
 /**
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 17201fbe0f..628c486081 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -298,6 +298,11 @@ EXPERIMENTAL {
 	rte_flow_get_q_aged_flows;
 	rte_mtr_meter_policy_get;
 	rte_mtr_meter_profile_get;
+
+	# added in 23.03
+	rte_flow_action_handle_query_update;
+	rte_flow_async_action_handle_query_update;
+
 };
 
 INTERNAL {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [PATCH v10 2/2] ethdev: add quota flow action and item
  2023-02-02 13:47 ` [PATCH v10 " Gregory Etelson
@ 2023-02-02 13:47   ` Gregory Etelson
  2023-02-19 17:04     ` Thomas Monjalon
  2023-02-07 16:03   ` [PATCH v10 1/2] ethdev: add query and update sync and async function calls Gregory Etelson
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-02-02 13:47 UTC (permalink / raw)
  To: dev
  Cc: getelson, matan, rasland, Ori Kam, Aman Singh, Yuying Zhang,
	Ferruh Yigit, Thomas Monjalon, Andrew Rybchenko

Quota action limits traffic according to pre-defined configuration.
Quota reflects overall traffic usage regardless bandwidth.
Quota flow action initialized with signed tokens number value.
Quota flow action updates tokens number according to
these rules:
1. if quota was configured to count packet length, for each packet
   of size S, tokens number reduced by S.
2. If quota was configured to count packets, each packet decrements
   tokens number.
quota action sets packet metadata according to a number of remaining
tokens number:
  PASS - remaining tokens number is non-negative.
  BLOCK - remaining tokens number is negative.

Quota flow item matches on that data

Application updates tokens number in quota flow action
with SET or ADD calls:
 SET(QUOTA, val) - arm quota with new tokens number set to val
 ADD(QUOTA, val) - increase existing quota tokens number by val

Both SET and ADD return to application number of tokens stored in port
before update.

If quota state was BLOCK (negative action tokens number)
application can change it to PASS after providing enough tokens to
raise action tokens number to 0 or above.

Application must create a rule with quota action to mark flow and
match on the mark with quota item in following flow rule.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
 app/test-pmd/cmdline_flow.c            | 394 ++++++++++++++++++++++++-
 app/test-pmd/config.c                  |  82 ++++-
 app/test-pmd/testpmd.h                 |  11 +
 doc/guides/nics/features/default.ini   |   2 +
 doc/guides/prog_guide/rte_flow.rst     |  41 +++
 doc/guides/rel_notes/release_23_03.rst |   9 +
 lib/ethdev/rte_flow.c                  |   2 +
 lib/ethdev/rte_flow.h                  | 123 ++++++++
 8 files changed, 654 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 88108498e0..34d28936b9 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -149,6 +149,7 @@ enum index {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Queue indirect action create arguments */
 	QUEUE_INDIRECT_ACTION_CREATE_ID,
@@ -168,6 +169,9 @@ enum index {
 	/* Queue indirect action query arguments */
 	QUEUE_INDIRECT_ACTION_QUERY_POSTPONE,
 
+	/* Queue indirect action query_update arguments */
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+
 	/* Push arguments. */
 	PUSH_QUEUE,
 
@@ -227,6 +231,7 @@ enum index {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 
 	/* Indirect action arguments */
@@ -234,6 +239,7 @@ enum index {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 
 	/* Indirect action create arguments */
 	INDIRECT_ACTION_CREATE_ID,
@@ -245,6 +251,10 @@ enum index {
 	/* Indirect action destroy arguments */
 	INDIRECT_ACTION_DESTROY_ID,
 
+	/* Indirect action query-and-update arguments */
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_QU_MODE_NAME,
+
 	/* Validate/create pattern. */
 	ITEM_PATTERN,
 	ITEM_PARAM_IS,
@@ -465,6 +475,9 @@ enum index {
 	ITEM_METER,
 	ITEM_METER_COLOR,
 	ITEM_METER_COLOR_NAME,
+	ITEM_QUOTA,
+	ITEM_QUOTA_STATE,
+	ITEM_QUOTA_STATE_NAME,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -621,6 +634,14 @@ enum index {
 	ACTION_REPRESENTED_PORT,
 	ACTION_REPRESENTED_PORT_ETHDEV_PORT_ID,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_QUOTA_CREATE_MODE_NAME,
+	ACTION_QUOTA_QU,
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_QUOTA_QU_UPDATE_OP_NAME,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -1011,6 +1032,7 @@ struct buffer {
 		} ia_destroy; /**< Indirect action destroy arguments. */
 		struct {
 			uint32_t action_id;
+			enum rte_flow_query_update_mode qu_mode;
 		} ia; /* Indirect action query arguments */
 		struct {
 			uint32_t table_id;
@@ -1097,6 +1119,7 @@ static const enum index next_config_attr[] = {
 	CONFIG_AGING_OBJECTS_NUMBER,
 	CONFIG_METERS_NUMBER,
 	CONFIG_CONN_TRACK_NUMBER,
+	CONFIG_QUOTAS_NUMBER,
 	CONFIG_FLAGS,
 	END,
 	ZERO,
@@ -1190,6 +1213,7 @@ static const enum index next_qia_subcmd[] = {
 	QUEUE_INDIRECT_ACTION_UPDATE,
 	QUEUE_INDIRECT_ACTION_DESTROY,
 	QUEUE_INDIRECT_ACTION_QUERY,
+	QUEUE_INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1231,6 +1255,25 @@ static const enum index next_ia_create_attr[] = {
 	ZERO,
 };
 
+static const enum index next_ia[] = {
+	INDIRECT_ACTION_ID2PTR,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index next_qia_qu_attr[] = {
+	QUEUE_INDIRECT_ACTION_QU_MODE,
+	QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
+static const enum index next_ia_qu_attr[] = {
+	INDIRECT_ACTION_QU_MODE,
+	INDIRECT_ACTION_SPEC,
+	ZERO
+};
+
 static const enum index next_dump_subcmd[] = {
 	DUMP_ALL,
 	DUMP_ONE,
@@ -1242,6 +1285,7 @@ static const enum index next_ia_subcmd[] = {
 	INDIRECT_ACTION_UPDATE,
 	INDIRECT_ACTION_DESTROY,
 	INDIRECT_ACTION_QUERY,
+	INDIRECT_ACTION_QUERY_UPDATE,
 	ZERO,
 };
 
@@ -1355,6 +1399,7 @@ static const enum index next_item[] = {
 	ITEM_L2TPV2,
 	ITEM_PPP,
 	ITEM_METER,
+	ITEM_QUOTA,
 	END_SET,
 	ZERO,
 };
@@ -1821,6 +1866,12 @@ static const enum index item_meter[] = {
 	ZERO,
 };
 
+static const enum index item_quota[] = {
+	ITEM_QUOTA_STATE,
+	ITEM_NEXT,
+	ZERO,
+};
+
 static const enum index next_action[] = {
 	ACTION_END,
 	ACTION_VOID,
@@ -1886,9 +1937,25 @@ static const enum index next_action[] = {
 	ACTION_PORT_REPRESENTOR,
 	ACTION_REPRESENTED_PORT,
 	ACTION_SEND_TO_KERNEL,
+	ACTION_QUOTA_CREATE,
+	ACTION_QUOTA_QU,
 	ZERO,
 };
 
+static const enum index action_quota_create[] = {
+	ACTION_QUOTA_CREATE_LIMIT,
+	ACTION_QUOTA_CREATE_MODE,
+	ACTION_NEXT,
+	ZERO
+};
+
+static const enum index action_quota_update[] = {
+	ACTION_QUOTA_QU_LIMIT,
+	ACTION_QUOTA_QU_UPDATE_OP,
+	ACTION_NEXT,
+	ZERO
+};
+
 static const enum index action_mark[] = {
 	ACTION_MARK_ID,
 	ACTION_NEXT,
@@ -2399,6 +2466,22 @@ static int parse_meter_policy_id2ptr(struct context *ctx,
 static int parse_meter_color(struct context *ctx, const struct token *token,
 			     const char *str, unsigned int len, void *buf,
 			     unsigned int size);
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size);
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size);
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size);
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size);
 static int comp_none(struct context *, const struct token *,
 		     unsigned int, char *, unsigned int);
 static int comp_boolean(struct context *, const struct token *,
@@ -2431,6 +2514,18 @@ static int comp_queue_id(struct context *, const struct token *,
 			 unsigned int, char *, unsigned int);
 static int comp_meter_color(struct context *, const struct token *,
 			    unsigned int, char *, unsigned int);
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size);
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size);
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2695,6 +2790,14 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer,
 					args.configure.port_attr.nb_aging_objects)),
 	},
+	[CONFIG_QUOTAS_NUMBER] = {
+		.name = "quotas_number",
+		.help = "number of quotas",
+		.next = NEXT(next_config_attr,
+			     NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct buffer,
+				     args.configure.port_attr.nb_quotas)),
+	},
 	[CONFIG_METERS_NUMBER] = {
 		.name = "meters_number",
 		.help = "number of meters",
@@ -3077,7 +3180,7 @@ static const struct token token_list[] = {
 		.help = "query indirect action",
 		.next = NEXT(next_qia_query_attr,
 			     NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-		.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_qia,
 	},
 	/* Indirect action destroy arguments. */
@@ -3097,6 +3200,21 @@ static const struct token token_list[] = {
 					    args.ia_destroy.action_id)),
 		.call = parse_qia_destroy,
 	},
+	[QUEUE_INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_qia
+	},
+	[QUEUE_INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "indirect query [and|or] update action",
+		.next = NEXT(next_qia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_qia
+	},
 	/* Indirect action update arguments. */
 	[QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
 		.name = "postpone",
@@ -3220,6 +3338,27 @@ static const struct token token_list[] = {
 		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
 		.call = parse_ia,
 	},
+	[INDIRECT_ACTION_QUERY_UPDATE] = {
+		.name = "query_update",
+		.help = "query [and|or] update",
+		.next = NEXT(next_ia_qu_attr, NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+		.call = parse_ia
+	},
+	[INDIRECT_ACTION_QU_MODE] = {
+		.name = "mode",
+		.help = "query_update mode",
+		.next = NEXT(next_ia_qu_attr,
+			     NEXT_ENTRY(INDIRECT_ACTION_QU_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct buffer, args.ia.qu_mode)),
+		.call = parse_ia,
+	},
+	[INDIRECT_ACTION_QU_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "query-update mode name",
+		.call = parse_qu_mode_name,
+		.comp = comp_qu_mode_name,
+	},
 	[VALIDATE] = {
 		.name = "validate",
 		.help = "check whether a flow rule can be created",
@@ -5127,6 +5266,26 @@ static const struct token token_list[] = {
 		.call = parse_meter_color,
 		.comp = comp_meter_color,
 	},
+	[ITEM_QUOTA] = {
+		.name = "quota",
+		.help = "match quota",
+		.priv = PRIV_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
+		.next = NEXT(item_quota),
+		.call = parse_vc
+	},
+	[ITEM_QUOTA_STATE] = {
+		.name = "quota_state",
+		.help = "quota state",
+		.next = NEXT(item_quota, NEXT_ENTRY(ITEM_QUOTA_STATE_NAME),
+			     NEXT_ENTRY(ITEM_PARAM_SPEC, ITEM_PARAM_MASK)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_quota, state))
+	},
+	[ITEM_QUOTA_STATE_NAME] = {
+		.name = "state_name",
+		.help = "quota state name",
+		.call = parse_quota_state_name,
+		.comp = comp_quota_state_name
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -6360,7 +6519,7 @@ static const struct token token_list[] = {
 		.name = "indirect",
 		.help = "apply indirect action by id",
 		.priv = PRIV_ACTION(INDIRECT, 0),
-		.next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_ID2PTR)),
+		.next = NEXT(next_ia),
 		.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),
 		.call = parse_vc,
 	},
@@ -6411,6 +6570,64 @@ static const struct token token_list[] = {
 		.help = "submit a list of associated actions for red",
 		.next = NEXT(next_action),
 	},
+	[ACTION_QUOTA_CREATE] = {
+		.name = "quota_create",
+		.help = "create quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_action_quota)),
+		.next = NEXT(action_quota_create),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_CREATE_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_create, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE] = {
+		.name = "mode",
+		.help = "quota mode",
+		.next = NEXT(action_quota_create,
+			     NEXT_ENTRY(ACTION_QUOTA_CREATE_MODE_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_quota, mode)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_CREATE_MODE_NAME] = {
+		.name = "mode_name",
+		.help = "quota mode name",
+		.call = parse_quota_mode_name,
+		.comp = comp_quota_mode_name
+	},
+	[ACTION_QUOTA_QU] = {
+		.name = "quota_update",
+		.help = "update quota action",
+		.priv = PRIV_ACTION(QUOTA,
+				    sizeof(struct rte_flow_update_quota)),
+		.next = NEXT(action_quota_update),
+		.call = parse_vc
+	},
+	[ACTION_QUOTA_QU_LIMIT] = {
+		.name = "limit",
+		.help = "quota limit",
+		.next = NEXT(action_quota_update, NEXT_ENTRY(COMMON_UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, quota)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP] = {
+		.name = "update_op",
+		.help = "query update op SET|ADD",
+		.next = NEXT(action_quota_update,
+			     NEXT_ENTRY(ACTION_QUOTA_QU_UPDATE_OP_NAME)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_update_quota, op)),
+		.call = parse_vc_conf
+	},
+	[ACTION_QUOTA_QU_UPDATE_OP_NAME] = {
+		.name = "update_op_name",
+		.help = "quota update op name",
+		.call = parse_quota_update_name,
+		.comp = comp_quota_update_name
+	},
 
 	/* Top-level command. */
 	[ADD] = {
@@ -6656,6 +6873,7 @@ parse_ia(struct context *ctx, const struct token *token,
 	switch (ctx->curr) {
 	case INDIRECT_ACTION_CREATE:
 	case INDIRECT_ACTION_UPDATE:
+	case INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6676,6 +6894,8 @@ parse_ia(struct context *ctx, const struct token *token,
 	case INDIRECT_ACTION_TRANSFER:
 		out->args.vc.attr.transfer = 1;
 		return len;
+	case INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -6748,6 +6968,7 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE:
 	case QUEUE_INDIRECT_ACTION_UPDATE:
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
 		out->args.vc.actions =
 			(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
 					       sizeof(double));
@@ -6770,6 +6991,8 @@ parse_qia(struct context *ctx, const struct token *token,
 		return len;
 	case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
 		return len;
+	case QUEUE_INDIRECT_ACTION_QU_MODE:
+		return len;
 	default:
 		return -1;
 	}
@@ -10052,6 +10275,107 @@ parse_meter_color(struct context *ctx, const struct token *token,
 	return len;
 }
 
+static int
+parse_name_to_index(struct context *ctx, const struct token *token,
+		    const char *str, unsigned int len, void *buf,
+		    unsigned int size,
+		    const char *const names[], size_t names_size, uint32_t *dst)
+{
+	int ret;
+	uint32_t i;
+
+	RTE_SET_USED(token);
+	RTE_SET_USED(buf);
+	RTE_SET_USED(size);
+	if (!ctx->object)
+		return len;
+	for (i = 0; i < names_size; i++) {
+		if (!names[i])
+			continue;
+		ret = strcmp_partial(names[i], str,
+				     RTE_MIN(len, strlen(names[i])));
+		if (!ret) {
+			*dst = i;
+			return len;
+		}
+	}
+	return -1;
+}
+
+static const char *const quota_mode_names[] = {
+	NULL,
+	[RTE_FLOW_QUOTA_MODE_PACKET] = "packet",
+	[RTE_FLOW_QUOTA_MODE_L2] = "l2",
+	[RTE_FLOW_QUOTA_MODE_L3] = "l3"
+};
+
+static const char *const quota_state_names[] = {
+	[RTE_FLOW_QUOTA_STATE_PASS] = "pass",
+	[RTE_FLOW_QUOTA_STATE_BLOCK] = "block"
+};
+
+static const char *const quota_update_names[] = {
+	[RTE_FLOW_UPDATE_QUOTA_SET] = "set",
+	[RTE_FLOW_UPDATE_QUOTA_ADD] = "add"
+};
+
+static const char *const query_update_mode_names[] = {
+	[RTE_FLOW_QU_QUERY_FIRST] = "query_first",
+	[RTE_FLOW_QU_UPDATE_FIRST] = "update_first"
+};
+
+static int
+parse_quota_state_name(struct context *ctx, const struct token *token,
+		       const char *str, unsigned int len, void *buf,
+		       unsigned int size)
+{
+	struct rte_flow_item_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names),
+				   (uint32_t *)&quota->state);
+}
+
+static int
+parse_quota_mode_name(struct context *ctx, const struct token *token,
+		      const char *str, unsigned int len, void *buf,
+		      unsigned int size)
+{
+	struct rte_flow_action_quota *quota = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names),
+				   (uint32_t *)&quota->mode);
+}
+
+static int
+parse_quota_update_name(struct context *ctx, const struct token *token,
+			const char *str, unsigned int len, void *buf,
+			unsigned int size)
+{
+	struct rte_flow_update_quota *update = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names),
+				   (uint32_t *)&update->op);
+}
+
+static int
+parse_qu_mode_name(struct context *ctx, const struct token *token,
+		   const char *str, unsigned int len, void *buf,
+		   unsigned int size)
+{
+	struct buffer *out = ctx->object;
+
+	return parse_name_to_index(ctx, token, str, len, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names),
+				   (uint32_t *)&out->args.ia.qu_mode);
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -10343,6 +10667,21 @@ comp_queue_id(struct context *ctx, const struct token *token,
 	return i;
 }
 
+static int
+comp_names_to_index(struct context *ctx, const struct token *token,
+		    unsigned int ent, char *buf, unsigned int size,
+		    const char *const names[], size_t names_size)
+{
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	if (!buf)
+		return names_size;
+	if (names[ent] && ent < names_size)
+		return rte_strscpy(buf, names[ent], size);
+	return -1;
+
+}
+
 /** Complete available Meter colors. */
 static int
 comp_meter_color(struct context *ctx, const struct token *token,
@@ -10357,6 +10696,42 @@ comp_meter_color(struct context *ctx, const struct token *token,
 	return -1;
 }
 
+static int
+comp_quota_state_name(struct context *ctx, const struct token *token,
+		      unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_state_names,
+				   RTE_DIM(quota_state_names));
+}
+
+static int
+comp_quota_mode_name(struct context *ctx, const struct token *token,
+		     unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_mode_names,
+				   RTE_DIM(quota_mode_names));
+}
+
+static int
+comp_quota_update_name(struct context *ctx, const struct token *token,
+		       unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   quota_update_names,
+				   RTE_DIM(quota_update_names));
+}
+
+static int
+comp_qu_mode_name(struct context *ctx, const struct token *token,
+		  unsigned int ent, char *buf, unsigned int size)
+{
+	return comp_names_to_index(ctx, token, ent, buf, size,
+				   query_update_mode_names,
+				   RTE_DIM(query_update_mode_names));
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -10715,7 +11090,14 @@ cmd_flow_parsed(const struct buffer *in)
 	case QUEUE_INDIRECT_ACTION_QUERY:
 		port_queue_action_handle_query(in->port,
 					       in->queue, in->postpone,
-					       in->args.vc.attr.group);
+					       in->args.ia.action_id);
+		break;
+	case QUEUE_INDIRECT_ACTION_QUERY_UPDATE:
+		port_queue_action_handle_query_update(in->port, in->queue,
+						      in->postpone,
+						      in->args.ia.action_id,
+						      in->args.ia.qu_mode,
+						      in->args.vc.actions);
 		break;
 	case INDIRECT_ACTION_CREATE:
 		port_action_handle_create(
@@ -10739,6 +11121,12 @@ cmd_flow_parsed(const struct buffer *in)
 	case INDIRECT_ACTION_QUERY:
 		port_action_handle_query(in->port, in->args.ia.action_id);
 		break;
+	case INDIRECT_ACTION_QUERY_UPDATE:
+		port_action_handle_query_update(in->port,
+						in->args.ia.action_id,
+						in->args.ia.qu_mode,
+						in->args.vc.actions);
+		break;
 	case VALIDATE:
 		port_flow_validate(in->port, &in->args.vc.attr,
 				   in->args.vc.pattern, in->args.vc.actions,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index acccb6b035..820ede5501 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1901,9 +1901,13 @@ port_action_handle_update(portid_t port_id, uint32_t id,
 }
 
 static void
-port_action_handle_query_dump(uint32_t type, union port_action_query *query)
+port_action_handle_query_dump(portid_t port_id,
+			      const struct port_indirect_action *pia,
+			      union port_action_query *query)
 {
-	switch (type) {
+	if (!pia || !query)
+		return;
+	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 		printf("Indirect AGE action:\n"
 		       " aged: %u\n"
@@ -1967,15 +1971,41 @@ port_action_handle_query_dump(uint32_t type, union port_action_query *query)
 		       query->ct.reply_dir.max_win,
 		       query->ct.reply_dir.max_ack);
 		break;
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
+		printf("Indirect QUOTA action %u\n"
+		       " unused quota: %" PRId64 "\n",
+		       pia->id, query->quota.quota);
+		break;
 	default:
-		fprintf(stderr,
-			"Indirect action (type: %d) doesn't support query\n",
-			type);
+		printf("port-%u: indirect action %u (type: %d) doesn't support query\n",
+		       pia->type, pia->id, port_id);
 		break;
 	}
 
 }
 
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia;
+	union port_action_query query;
+
+	pia = action_get_by_id(port_id, id);
+	if (!pia || !pia->handle)
+		return;
+	ret = rte_flow_action_handle_query_update(port_id, pia->handle, action,
+						  &query, qu_mode, &error);
+	if (ret)
+		port_flow_complain(&error);
+	else
+		port_action_handle_query_dump(port_id, pia, &query);
+
+}
+
 int
 port_action_handle_query(portid_t port_id, uint32_t id)
 {
@@ -1989,6 +2019,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	switch (pia->type) {
 	case RTE_FLOW_ACTION_TYPE_AGE:
 	case RTE_FLOW_ACTION_TYPE_COUNT:
+	case RTE_FLOW_ACTION_TYPE_QUOTA:
 		break;
 	default:
 		fprintf(stderr,
@@ -2001,7 +2032,7 @@ port_action_handle_query(portid_t port_id, uint32_t id)
 	memset(&query, 0, sizeof(query));
 	if (rte_flow_action_handle_query(port_id, pia->handle, &query, &error))
 		return port_flow_complain(&error);
-	port_action_handle_query_dump(pia->type, &query);
+	port_action_handle_query_dump(port_id, pia, &query);
 	return 0;
 }
 
@@ -2945,6 +2976,42 @@ port_queue_action_handle_update(portid_t port_id,
 	return 0;
 }
 
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action)
+{
+	int ret;
+	struct rte_flow_error error;
+	struct port_indirect_action *pia = action_get_by_id(port_id, id);
+	const struct rte_flow_op_attr attr = { .postpone = postpone};
+	struct queue_job *job;
+
+	if (!pia || !pia->handle)
+		return;
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return;
+	job->type = QUEUE_JOB_TYPE_ACTION_QUERY;
+	job->pia = pia;
+
+	ret = rte_flow_async_action_handle_query_update(port_id, queue_id,
+							&attr, pia->handle,
+							action,
+							&job->query,
+							qu_mode, job,
+							&error);
+	if (ret) {
+		port_flow_complain(&error);
+		free(job);
+	} else {
+		printf("port-%u: indirect action #%u update-and-query queued\n",
+		       port_id, id);
+	}
+}
+
 /** Enqueue indirect action query operation. */
 int
 port_queue_action_handle_query(portid_t port_id,
@@ -3215,7 +3282,8 @@ port_queue_flow_pull(portid_t port_id, queueid_t queue_id)
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_DESTROY)
 			free(job->pia);
 		else if (job->type == QUEUE_JOB_TYPE_ACTION_QUERY)
-			port_action_handle_query_dump(job->pia->type, &job->query);
+			port_action_handle_query_dump(port_id, job->pia,
+						      &job->query);
 		free(job);
 	}
 	printf("Queue #%u pulled %u operations (%u failed, %u succeeded)\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 7d24d25970..babc94246f 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -236,6 +236,7 @@ union port_action_query {
 	struct rte_flow_query_count count;
 	struct rte_flow_query_age age;
 	struct rte_flow_action_conntrack ct;
+	struct rte_flow_query_quota quota;
 };
 
 /* Descriptor for queue job. */
@@ -904,6 +905,10 @@ struct rte_flow_action_handle *port_action_handle_get_by_id(portid_t port_id,
 							    uint32_t id);
 int port_action_handle_update(portid_t port_id, uint32_t id,
 			      const struct rte_flow_action *action);
+void
+port_action_handle_query_update(portid_t port_id, uint32_t id,
+				enum rte_flow_query_update_mode qu_mode,
+				const struct rte_flow_action *action);
 int port_flow_get_info(portid_t port_id);
 int port_flow_configure(portid_t port_id,
 			const struct rte_flow_port_attr *port_attr,
@@ -948,6 +953,12 @@ int port_queue_action_handle_update(portid_t port_id, uint32_t queue_id,
 				    const struct rte_flow_action *action);
 int port_queue_action_handle_query(portid_t port_id, uint32_t queue_id,
 				   bool postpone, uint32_t id);
+void
+port_queue_action_handle_query_update(portid_t port_id,
+				      uint32_t queue_id, bool postpone,
+				      uint32_t id,
+				      enum rte_flow_query_update_mode qu_mode,
+				      const struct rte_flow_action *action);
 int port_queue_flow_push(portid_t port_id, queueid_t queue_id);
 int port_queue_flow_pull(portid_t port_id, queueid_t queue_id);
 void port_queue_flow_aged(portid_t port_id, uint32_t queue_id, uint8_t destroy);
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index 510cc6679d..92d8765fd5 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -132,6 +132,7 @@ ppp                  =
 pppoed               =
 pppoes               =
 pppoe_proto_id       =
+quota                =
 raw                  =
 represented_port     =
 sctp                 =
@@ -178,6 +179,7 @@ pf                   =
 port_id              =
 port_representor     =
 queue                =
+quota                =
 raw_decap            =
 raw_encap            =
 represented_port     =
diff --git a/doc/guides/prog_guide/rte_flow.rst b/doc/guides/prog_guide/rte_flow.rst
index 3e6242803d..7a3868638c 100644
--- a/doc/guides/prog_guide/rte_flow.rst
+++ b/doc/guides/prog_guide/rte_flow.rst
@@ -1544,6 +1544,13 @@ Matches Color Marker set by a Meter.
 
 - ``color``: Metering color marker.
 
+Item: ``QUOTA``
+^^^^^^^^^^^^^^^
+
+Matches flow quota state set by quota action.
+
+- ``state``: Flow quota state
+
 Actions
 ~~~~~~~
 
@@ -3227,6 +3234,40 @@ and rte_mtr_policy_get() API respectively.
    | ``policy``       | Meter policy object  |
    +------------------+----------------------+
 
+Action: ``QUOTA``
+^^^^^^^^^^^^^^^^^
+
+Update ``quota`` value and set packet quota state.
+If the ``quota`` value after update is non-negative packet quota state set to
+``RTE_FLOW_QUOTA_STATE_PASS``. Otherwise, packet quota state set to ``RTE_FLOW_QUOTA_STATE_BLOCK``.
+The ``quota`` value is reduced according to ``mode`` setting.
+
+.. _table_rte_flow_action_quota:
+
+.. table:: QUOTA
+
+   +------------------+------------------------+
+   | Field            | Value                  |
+   +==================+========================+
+   | ``mode``         | Quota operational mode |
+   +------------------+------------------------+
+   | ``quota``        | Quota value            |
+   +------------------+------------------------+
+
+.. _rte_flow_quota_mode:
+
+.. table:: Quota update modes
+
+   +---------------------------------+-------------------------------------+
+   | Value                           | Description                         |
+   +=================================+=====================================+
+   | ``RTE_FLOW_QUOTA_MODE_PACKET``  | Count packets                       |
+   +---------------------------------+-------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L2``      | Count packet bytes starting from L2 |
+   +------------------+----------------------------------------------------+
+   | ``RTE_FLOW_QUOTA_MODE_L3``      | Count packet bytes starting from L3 |
+   +------------------+----------------------------------------------------+
+
 Negative types
 ~~~~~~~~~~~~~~
 
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 5ab90f5118..8ffc056480 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -86,6 +86,15 @@ New Features
   - ``rte_flow_action_handle_query_update``
   - ``rte_flow_async_action_handle_query_update``
 
+* **Added quota flow action and quota flow item.**
+
+  - ``RTE_FLOW_ACTION_TYPE_QUOTA``
+  - ``RTE_FLOW_ITEM_TYPE_QUOTA``
+
+* **Updated testpmd to support quota flow action and item.**
+
+  Added support for flow quota action and item.
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
index 923d795be1..6c490ccadc 100644
--- a/lib/ethdev/rte_flow.c
+++ b/lib/ethdev/rte_flow.c
@@ -157,6 +157,7 @@ static const struct rte_flow_desc_data rte_flow_desc_item[] = {
 	MK_FLOW_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
 	MK_FLOW_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
 	MK_FLOW_ITEM(METER_COLOR, sizeof(struct rte_flow_item_meter_color)),
+	MK_FLOW_ITEM(QUOTA, sizeof(struct rte_flow_item_quota)),
 };
 
 /** Generate flow_action[] entry. */
@@ -251,6 +252,7 @@ static const struct rte_flow_desc_data rte_flow_desc_action[] = {
 	MK_FLOW_ACTION(REPRESENTED_PORT, sizeof(struct rte_flow_action_ethdev)),
 	MK_FLOW_ACTION(METER_MARK, sizeof(struct rte_flow_action_meter_mark)),
 	MK_FLOW_ACTION(SEND_TO_KERNEL, 0),
+	MK_FLOW_ACTION(QUOTA, sizeof(struct rte_flow_action_quota)),
 };
 
 int
diff --git a/lib/ethdev/rte_flow.h b/lib/ethdev/rte_flow.h
index b754330579..58a56168ab 100644
--- a/lib/ethdev/rte_flow.h
+++ b/lib/ethdev/rte_flow.h
@@ -624,7 +624,45 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_meter_color.
 	 */
 	RTE_FLOW_ITEM_TYPE_METER_COLOR,
+
+	/**
+	 * Match Quota state
+	 *
+	 * @see struct rte_flow_item_quota
+	 */
+	 RTE_FLOW_ITEM_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA state.
+ *
+ * @see struct rte_flow_item_quota
+ */
+enum rte_flow_quota_state {
+	RTE_FLOW_QUOTA_STATE_PASS, /**< PASS quota state */
+	RTE_FLOW_QUOTA_STATE_BLOCK /**< BLOCK quota state */
+};
+
+/**
+ * RTE_FLOW_ITEM_TYPE_QUOTA
+ *
+ * Matches QUOTA state
+ */
+struct rte_flow_item_quota {
+	enum rte_flow_quota_state state;
+};
+
+/**
+ * Default mask for RTE_FLOW_ITEM_TYPE_QUOTA
+ */
+#ifndef __cplusplus
+static const struct rte_flow_item_quota rte_flow_item_quota_mask = {
+	.state = (enum rte_flow_quota_state)0xff
 };
+#endif
 
 /**
  *
@@ -2736,6 +2774,81 @@ enum rte_flow_action_type {
 	 * No associated configuration structure.
 	 */
 	RTE_FLOW_ACTION_TYPE_SEND_TO_KERNEL,
+
+	/**
+	 * Apply the quota verdict (PASS or BLOCK) to a flow.
+	 *
+	 * @see struct rte_flow_action_quota
+	 * @see struct rte_flow_query_quota
+	 * @see struct rte_flow_update_quota
+	 */
+	 RTE_FLOW_ACTION_TYPE_QUOTA,
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * QUOTA operational mode.
+ *
+ * @see struct rte_flow_action_quota
+ */
+enum rte_flow_quota_mode {
+	RTE_FLOW_QUOTA_MODE_PACKET = 1, /**< Count packets. */
+	RTE_FLOW_QUOTA_MODE_L2 = 2, /**< Count packet bytes starting from L2. */
+	RTE_FLOW_QUOTA_MODE_L3 = 3, /**< Count packet bytes starting from L3. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ */
+struct rte_flow_action_quota {
+	enum rte_flow_quota_mode mode; /**< Quota operational mode. */
+	int64_t quota;                 /**< Quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query indirect QUOTA action.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ */
+struct rte_flow_query_quota {
+	int64_t quota; /**< Quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Indirect QUOTA update operations.
+ *
+ * @see struct rte_flow_update_quota
+ */
+enum rte_flow_update_quota_op {
+	RTE_FLOW_UPDATE_QUOTA_SET, /**< Set new quota value. */
+	RTE_FLOW_UPDATE_QUOTA_ADD, /**< Increase quota value. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @see RTE_FLOW_ACTION_TYPE_QUOTA
+ *
+ * Update indirect QUOTA action.
+ */
+struct rte_flow_update_quota {
+	enum rte_flow_update_quota_op op; /**< Update operation. */
+	int64_t quota;                    /**< Quota value. */
 };
 
 /**
@@ -4854,6 +4967,11 @@ struct rte_flow_port_info {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t max_nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t max_nb_quotas;
 	/**
 	 * Port supported flags (RTE_FLOW_PORT_FLAG_*).
 	 */
@@ -4932,6 +5050,11 @@ struct rte_flow_port_attr {
 	 * @see RTE_FLOW_ACTION_TYPE_CONNTRACK
 	 */
 	uint32_t nb_conn_tracks;
+	/**
+	 * Maximum number of quota actions.
+	 * @see RTE_FLOW_ACTION_TYPE_QUOTA
+	 */
+	uint32_t nb_quotas;
 	/**
 	 * Port flags (RTE_FLOW_PORT_FLAG_*).
 	 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 58+ messages in thread

* RE: [PATCH v10 1/2] ethdev: add query and update sync and async function calls
  2023-02-02 13:47 ` [PATCH v10 " Gregory Etelson
  2023-02-02 13:47   ` [PATCH v10 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-02-07 16:03   ` Gregory Etelson
  2023-02-09 15:13   ` Ferruh Yigit
  2023-02-16 15:43   ` Ferruh Yigit
  3 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-07 16:03 UTC (permalink / raw)
  To: NBU-Contact-Thomas Monjalon (EXTERNAL), Ferruh Yigit, Andrew Rybchenko
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam, dev

Hello,

Please merge the patches in this series,
if there are no more comments.

Regards,
Gregory



^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v10 1/2] ethdev: add query and update sync and async function calls
  2023-02-02 13:47 ` [PATCH v10 " Gregory Etelson
  2023-02-02 13:47   ` [PATCH v10 2/2] ethdev: add quota flow action and item Gregory Etelson
  2023-02-07 16:03   ` [PATCH v10 1/2] ethdev: add query and update sync and async function calls Gregory Etelson
@ 2023-02-09 15:13   ` Ferruh Yigit
  2023-02-09 16:10     ` Gregory Etelson
  2023-02-12 11:13     ` Gregory Etelson
  2023-02-16 15:43   ` Ferruh Yigit
  3 siblings, 2 replies; 58+ messages in thread
From: Ferruh Yigit @ 2023-02-09 15:13 UTC (permalink / raw)
  To: Gregory Etelson, dev, Andrew Rybchenko
  Cc: matan, rasland, Ori Kam, Thomas Monjalon

On 2/2/2023 1:47 PM, Gregory Etelson wrote:
> Current API allows either query or update indirect flow action.
> If indirect action must be conditionally updated according to it's
> present state application must first issue action query then
> analyze returned data and if needed issue update request.
> When the update will be processed, action state can change and
> the update can invalidate the action.
> 
> Add `rte_flow_action_handle_query_update` function call,
> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.
> 
> Application can control query and update order, if that is supported
> by port hardware, by setting `qu_mode` parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>
> ---
> v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> v3: Update release release notes.
>     Fix doxygen errors.
> v4: Add returned errno codes.
> v5: Update the patch description.
>     Fix typos.
> v6: Resolve merge conflict with the main branch.	
> v7: Update documentation.
> v8: Style fixes.
> v9: Add parameters validation.
> v10: Code update.

it seems there were some comments from Andrew to previous version, are
they resolved now?

@Andrew, do you have any objection/comment on this version?

^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v10 1/2] ethdev: add query and update sync and async function calls
  2023-02-09 15:13   ` Ferruh Yigit
@ 2023-02-09 16:10     ` Gregory Etelson
  2023-02-12 11:13     ` Gregory Etelson
  1 sibling, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-09 16:10 UTC (permalink / raw)
  To: Ferruh Yigit, dev, Andrew Rybchenko
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL)

Hello Ferruh,
 
[]
> 
> it seems there were some comments from Andrew to previous version, are
> they resolved now?
> 

I resolved all outstanding comments

> @Andrew, do you have any objection/comment on this version?

Regards,
Gregory

^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v9 1/2] ethdev: add query and update sync and async function calls
  2023-02-02 12:13   ` [PATCH v9 1/2] ethdev: add query and update sync and async function calls Andrew Rybchenko
@ 2023-02-09 19:16     ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-09 19:16 UTC (permalink / raw)
  To: Andrew Rybchenko, dev
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Ferruh Yigit

Hello Andrew,

> On 2/2/23 14:54, Gregory Etelson wrote:
> > diff --git a/lib/ethdev/rte_flow.c b/lib/ethdev/rte_flow.c
> > index 7d0c24366c..4554c8f021 100644
> > --- a/lib/ethdev/rte_flow.c
> > +++ b/lib/ethdev/rte_flow.c
> > @@ -1883,3 +1883,85 @@ rte_flow_async_action_handle_query(uint16_t
> port_id,
> >                                         action_handle, data, user_data, error);
> >       return flow_err(port_id, ret, error);
> >   }
> > +
> > +int
> > +rte_flow_action_handle_query_update(uint16_t port_id,
> > +                                 struct rte_flow_action_handle *handle,
> > +                                 const void *update, void *query,
> > +                                 enum rte_flow_query_update_mode mode,
> > +                                 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);
> > +     if (!handle)
> > +             return -EINVAL;
> > +     dev = &rte_eth_devices[port_id];
> > +     ops = rte_flow_ops_get(port_id, error);
> > +     if (!ops)
> > +             return -ENOTSUP;
> > +     if (update && query) {
> > +             if (!ops->action_handle_query_update)
> > +                     return -ENOTSUP;
> > +             if (mode != RTE_FLOW_QU_QUERY_FIRST &&
> > +                 mode != RTE_FLOW_QU_UPDATE_FIRST)
> > +                     return -EINVAL;
> 
> Shouldn't it be checked in any case?
> Also I'd initialize RTE_FLOW_QU_QUERY_FIRST to 1 on enum
> definition to ensure that just 0 is not used as a mode value.
> Also "Required if both *update* and *query* are not NULL." does
> not make sense in the description since you have no way to skip
> it.
> 

Fixed in v10.

> > +             ret = ops->action_handle_query_update(dev, handle, update,
> > +                                                   query, mode, error);
> > +     } else if (!update && query) {
> > +             ret = rte_flow_action_handle_query(port_id, handle, query,
> > +                                                error);
> > +     } else if (update && !query) {
> > +             ret = rte_flow_action_handle_update(port_id, handle, update,
> > +                                                 error);
> > +     } else {
> > +             return -EINVAL;
> > +     }
> 
> IMHO logic is wrong above since it should be sufficient to
> implement just one action_handle_query_update callback instead
> of all three.
> 
> I.e. if action_handle_query_update is available, it should be
> used in any case.
> 

Fixed in v10.

> > +     return flow_err(port_id, ret, error);
> > +}
> >   };
> >
> >   INTERNAL {
> 
> same for async API

Fixed in v10.


^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v10 1/2] ethdev: add query and update sync and async function calls
  2023-02-09 15:13   ` Ferruh Yigit
  2023-02-09 16:10     ` Gregory Etelson
@ 2023-02-12 11:13     ` Gregory Etelson
  2023-02-13 12:30       ` Gregory Etelson
  1 sibling, 1 reply; 58+ messages in thread
From: Gregory Etelson @ 2023-02-12 11:13 UTC (permalink / raw)
  To: Ferruh Yigit, dev, Andrew Rybchenko
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL)

Hello Ferruh,

What holds merge of https://patches.dpdk.org/project/dpdk/list/?series=26763 ?

Regards,
Gregory

> -----Original Message-----
> From: Ferruh Yigit <ferruh.yigit@amd.com>
> Sent: Thursday, 9 February 2023 17:14
> To: Gregory Etelson <getelson@nvidia.com>; dev@dpdk.org; Andrew
> Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Cc: Matan Azrad <matan@nvidia.com>; Raslan Darawsheh
> <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; NBU-Contact-Thomas
> Monjalon (EXTERNAL) <thomas@monjalon.net>
> Subject: Re: [PATCH v10 1/2] ethdev: add query and update sync and async
> function calls
> 
> External email: Use caution opening links or attachments
> 
> 
> On 2/2/2023 1:47 PM, Gregory Etelson wrote:
> > Current API allows either query or update indirect flow action.
> > If indirect action must be conditionally updated according to it's
> > present state application must first issue action query then
> > analyze returned data and if needed issue update request.
> > When the update will be processed, action state can change and
> > the update can invalidate the action.
> >
> > Add `rte_flow_action_handle_query_update` function call,
> > and it's async version `rte_flow_async_action_handle_query_update`
> > to atomically query and update flow action.
> >
> > Application can control query and update order, if that is supported
> > by port hardware, by setting `qu_mode` parameter to
> > RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> >
> > Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> > Acked-by: Ori Kam <orika@nvidia.com>
> > ---
> > v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> > v3: Update release release notes.
> >     Fix doxygen errors.
> > v4: Add returned errno codes.
> > v5: Update the patch description.
> >     Fix typos.
> > v6: Resolve merge conflict with the main branch.
> > v7: Update documentation.
> > v8: Style fixes.
> > v9: Add parameters validation.
> > v10: Code update.
> 
> it seems there were some comments from Andrew to previous version, are
> they resolved now?
> 
> @Andrew, do you have any objection/comment on this version?

^ permalink raw reply	[flat|nested] 58+ messages in thread

* RE: [PATCH v10 1/2] ethdev: add query and update sync and async function calls
  2023-02-12 11:13     ` Gregory Etelson
@ 2023-02-13 12:30       ` Gregory Etelson
  0 siblings, 0 replies; 58+ messages in thread
From: Gregory Etelson @ 2023-02-13 12:30 UTC (permalink / raw)
  To: Ferruh Yigit, dev, Andrew Rybchenko
  Cc: Matan Azrad, Raslan Darawsheh, Ori Kam,
	NBU-Contact-Thomas Monjalon (EXTERNAL),
	Lior Margalit

Hello Andrew,

What holds merge of https://patches.dpdk.org/project/dpdk/list/?series=26763 ?

Regards,
Gregory

> -----Original Message-----
> From: Gregory Etelson
> Sent: Sunday, 12 February 2023 13:13
> To: Ferruh Yigit <ferruh.yigit@amd.com>; dev@dpdk.org; Andrew
> Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Cc: Matan Azrad <matan@nvidia.com>; Raslan Darawsheh
> <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; NBU-Contact-Thomas
> Monjalon (EXTERNAL) <thomas@monjalon.net>
> Subject: RE: [PATCH v10 1/2] ethdev: add query and update sync and async
> function calls
> 
> Hello Ferruh,
> 
> What holds merge of
> https://patches.dpdk.org/project/dpdk/list/?series=26763 ?
> 
> Regards,
> Gregory
> 
> > -----Original Message-----
> > From: Ferruh Yigit <ferruh.yigit@amd.com>
> > Sent: Thursday, 9 February 2023 17:14
> > To: Gregory Etelson <getelson@nvidia.com>; dev@dpdk.org; Andrew
> > Rybchenko <andrew.rybchenko@oktetlabs.ru>
> > Cc: Matan Azrad <matan@nvidia.com>; Raslan Darawsheh
> > <rasland@nvidia.com>; Ori Kam <orika@nvidia.com>; NBU-Contact-
> Thomas
> > Monjalon (EXTERNAL) <thomas@monjalon.net>
> > Subject: Re: [PATCH v10 1/2] ethdev: add query and update sync and
> async
> > function calls
> >
> > External email: Use caution opening links or attachments
> >
> >
> > On 2/2/2023 1:47 PM, Gregory Etelson wrote:
> > > Current API allows either query or update indirect flow action.
> > > If indirect action must be conditionally updated according to it's
> > > present state application must first issue action query then
> > > analyze returned data and if needed issue update request.
> > > When the update will be processed, action state can change and
> > > the update can invalidate the action.
> > >
> > > Add `rte_flow_action_handle_query_update` function call,
> > > and it's async version `rte_flow_async_action_handle_query_update`
> > > to atomically query and update flow action.
> > >
> > > Application can control query and update order, if that is supported
> > > by port hardware, by setting `qu_mode` parameter to
> > > RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> > >
> > > Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> > > Acked-by: Ori Kam <orika@nvidia.com>
> > > ---
> > > v2: Remove RTE_FLOW_QU_DEFAULT query-update mode.
> > > v3: Update release release notes.
> > >     Fix doxygen errors.
> > > v4: Add returned errno codes.
> > > v5: Update the patch description.
> > >     Fix typos.
> > > v6: Resolve merge conflict with the main branch.
> > > v7: Update documentation.
> > > v8: Style fixes.
> > > v9: Add parameters validation.
> > > v10: Code update.
> >
> > it seems there were some comments from Andrew to previous version,
> are
> > they resolved now?
> >
> > @Andrew, do you have any objection/comment on this version?

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v10 1/2] ethdev: add query and update sync and async function calls
  2023-02-02 13:47 ` [PATCH v10 " Gregory Etelson
                     ` (2 preceding siblings ...)
  2023-02-09 15:13   ` Ferruh Yigit
@ 2023-02-16 15:43   ` Ferruh Yigit
  3 siblings, 0 replies; 58+ messages in thread
From: Ferruh Yigit @ 2023-02-16 15:43 UTC (permalink / raw)
  To: Gregory Etelson, dev
  Cc: matan, rasland, Ori Kam, Thomas Monjalon, Andrew Rybchenko

On 2/2/2023 1:47 PM, Gregory Etelson wrote:
> Current API allows either query or update indirect flow action.
> If indirect action must be conditionally updated according to it's
> present state application must first issue action query then
> analyze returned data and if needed issue update request.
> When the update will be processed, action state can change and
> the update can invalidate the action.
> 
> Add `rte_flow_action_handle_query_update` function call,
> and it's async version `rte_flow_async_action_handle_query_update`
> to atomically query and update flow action.
> 
> Application can control query and update order, if that is supported
> by port hardware, by setting `qu_mode` parameter to
> RTE_FLOW_QU_QUERY_FIRST or RTE_FLOW_QU_UPDATE_FIRST.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>


Series applied to dpdk-next-net/main, thanks.

<...>


> --- a/doc/guides/rel_notes/release_23_03.rst
> +++ b/doc/guides/rel_notes/release_23_03.rst
> @@ -78,6 +78,14 @@ New Features
>      ``rte_event_dev_config::nb_single_link_event_port_queues`` parameter
>      required for eth_rx, eth_tx, crypto and timer eventdev adapters.
>  
> +* **Added functions to atomically query and update indirect flow action.**
> +
> +  Added synchronous and asynchronous functions to atomically query and update
> +  indirect flow action:
> +
> +  - ``rte_flow_action_handle_query_update``
> +  - ``rte_flow_async_action_handle_query_update``
> +
>  

I updated the location of release notes update while merging, but can
you please pay attention to the order documented in the section comment
next time? Same for both patches.


^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [PATCH v10 2/2] ethdev: add quota flow action and item
  2023-02-02 13:47   ` [PATCH v10 2/2] ethdev: add quota flow action and item Gregory Etelson
@ 2023-02-19 17:04     ` Thomas Monjalon
  0 siblings, 0 replies; 58+ messages in thread
From: Thomas Monjalon @ 2023-02-19 17:04 UTC (permalink / raw)
  To: Ori Kam, Gregory Etelson
  Cc: dev, matan, rasland, Aman Singh, Yuying Zhang, Ferruh Yigit,
	Andrew Rybchenko

02/02/2023 14:47, Gregory Etelson:
> Quota action limits traffic according to pre-defined configuration.
> Quota reflects overall traffic usage regardless bandwidth.
> Quota flow action initialized with signed tokens number value.
> Quota flow action updates tokens number according to
> these rules:
> 1. if quota was configured to count packet length, for each packet
>    of size S, tokens number reduced by S.
> 2. If quota was configured to count packets, each packet decrements
>    tokens number.
> quota action sets packet metadata according to a number of remaining
> tokens number:
>   PASS - remaining tokens number is non-negative.
>   BLOCK - remaining tokens number is negative.
> 
> Quota flow item matches on that data
> 
> Application updates tokens number in quota flow action
> with SET or ADD calls:
>  SET(QUOTA, val) - arm quota with new tokens number set to val
>  ADD(QUOTA, val) - increase existing quota tokens number by val
> 
> Both SET and ADD return to application number of tokens stored in port
> before update.
> 
> If quota state was BLOCK (negative action tokens number)
> application can change it to PASS after providing enough tokens to
> raise action tokens number to 0 or above.
> 
> Application must create a rule with quota action to mark flow and
> match on the mark with quota item in following flow rule.
> 
> Signed-off-by: Gregory Etelson <getelson@nvidia.com>
> Acked-by: Ori Kam <orika@nvidia.com>

I feel this kind of explanation would have been better in rte_flow.rst
instead of a "doxygen-like" documentation.

Greogory, Ori, feels lucky, you've just got promoted as the rewriters
of the whole rte_flow.rst :)



^ permalink raw reply	[flat|nested] 58+ messages in thread

end of thread, other threads:[~2023-02-19 17:04 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-21  7:35 [PATCH 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
2022-12-21  7:35 ` [PATCH 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-01-08 13:47   ` Ori Kam
2023-01-04  9:56 ` [PATCH 1/2] ethdev: add query_update sync and async function calls Ori Kam
2023-01-11  9:28   ` Gregory Etelson
2023-01-11  9:22 ` [PATCH v2 " Gregory Etelson
2023-01-11  9:22   ` [PATCH v2 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-01-11 12:20 ` [PATCH v3 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
2023-01-11 12:20   ` [PATCH v3 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-01-18 10:31 ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
2023-01-18 10:31   ` [PATCH v4 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-01-18 14:03     ` Thomas Monjalon
2023-01-19  9:13       ` Gregory Etelson
2023-01-19  9:31         ` Thomas Monjalon
2023-01-19  9:39           ` Gregory Etelson
2023-01-18 13:56   ` [PATCH v4 1/2] ethdev: add query_update sync and async function calls Thomas Monjalon
2023-01-18 17:34     ` Gregory Etelson
2023-01-19  8:44       ` Thomas Monjalon
2023-01-19 13:25 ` [PATCH v5 " Gregory Etelson
2023-01-19 13:25   ` [PATCH v5 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-01-19 16:47 ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Gregory Etelson
2023-01-19 16:47   ` [PATCH v6 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-01-20  8:52     ` Andrew Rybchenko
2023-01-24  9:26       ` Gregory Etelson
2023-01-20  8:35   ` [PATCH v6 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
2023-01-20 10:46     ` Gregory Etelson
2023-01-20 11:22       ` Andrew Rybchenko
2023-01-20 16:50         ` Gregory Etelson
2023-02-01 11:00           ` Andrew Rybchenko
2023-02-01 14:03             ` Gregory Etelson
2023-01-24  9:37 ` [PATCH v7 " Gregory Etelson
2023-01-24  9:37   ` [PATCH v7 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-02-01 11:22     ` Andrew Rybchenko
2023-02-01 15:09       ` Gregory Etelson
2023-02-01 11:16   ` [PATCH v7 1/2] ethdev: add query_update sync and async function calls Andrew Rybchenko
2023-02-01 14:52     ` Gregory Etelson
2023-02-01 15:16 ` [PATCH v8 " Gregory Etelson
2023-02-01 15:16   ` [PATCH v8 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-02-02  9:17     ` Andrew Rybchenko
2023-02-01 17:30   ` [PATCH v8 1/2] ethdev: add query_update sync and async function calls Ori Kam
2023-02-02  9:15   ` Andrew Rybchenko
2023-02-02 10:24     ` Gregory Etelson
2023-02-02 10:30       ` Andrew Rybchenko
2023-02-02 10:44         ` Gregory Etelson
2023-02-02 10:47           ` Andrew Rybchenko
2023-02-02 11:54 ` [PATCH v9 1/2] ethdev: add query and update " Gregory Etelson
2023-02-02 11:54   ` [PATCH v9 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-02-02 12:13   ` [PATCH v9 1/2] ethdev: add query and update sync and async function calls Andrew Rybchenko
2023-02-09 19:16     ` Gregory Etelson
2023-02-02 13:47 ` [PATCH v10 " Gregory Etelson
2023-02-02 13:47   ` [PATCH v10 2/2] ethdev: add quota flow action and item Gregory Etelson
2023-02-19 17:04     ` Thomas Monjalon
2023-02-07 16:03   ` [PATCH v10 1/2] ethdev: add query and update sync and async function calls Gregory Etelson
2023-02-09 15:13   ` Ferruh Yigit
2023-02-09 16:10     ` Gregory Etelson
2023-02-12 11:13     ` Gregory Etelson
2023-02-13 12:30       ` Gregory Etelson
2023-02-16 15:43   ` Ferruh Yigit

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.