[v2,30/34] net/sfc: support indirect count action in transfer flows

Message ID 20230604000051.7877-31-ivan.malov@arknetworks.am (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series net/sfc: support HW conntrack assistance |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Ivan Malov June 4, 2023, midnight UTC
  Indirect count action is useful to applications that
need to gather aggregated statistics for many flows.

Signed-off-by: Ivan Malov <ivan.malov@arknetworks.am>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
---
 doc/guides/nics/sfc_efx.rst            |   2 +
 doc/guides/rel_notes/release_23_07.rst |   3 +
 drivers/net/sfc/sfc.h                  |   1 +
 drivers/net/sfc/sfc_flow.c             | 126 +++++++++++++++++++
 drivers/net/sfc/sfc_flow.h             |  14 +++
 drivers/net/sfc/sfc_mae.c              | 167 ++++++++++++++++++++++++-
 drivers/net/sfc/sfc_mae.h              |  15 +++
 7 files changed, 327 insertions(+), 1 deletion(-)
  

Patch

diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index 6e974c3720..ba82b02093 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -306,6 +306,8 @@  Supported actions (***transfer*** rules):
 
 - COUNT
 
+- INDIRECT
+
 - DROP
 
 Validating flow rules depends on the firmware variant.
diff --git a/doc/guides/rel_notes/release_23_07.rst b/doc/guides/rel_notes/release_23_07.rst
index 058897219e..4feffaff40 100644
--- a/doc/guides/rel_notes/release_23_07.rst
+++ b/doc/guides/rel_notes/release_23_07.rst
@@ -98,6 +98,9 @@  New Features
     The caller is responsible to request this offload
     only when the target header is an IPv4-based one.
 
+  * Added support for transfer flow action INDIRECT
+    with subtype COUNT, for aggregated statistics.
+
 
 Removed Items
 -------------
diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h
index 6b301aad60..f84a21009e 100644
--- a/drivers/net/sfc/sfc.h
+++ b/drivers/net/sfc/sfc.h
@@ -248,6 +248,7 @@  struct sfc_adapter {
 	struct sfc_tbls			hw_tables;
 	struct sfc_repr_proxy		repr_proxy;
 
+	struct sfc_flow_indir_actions	flow_indir_actions;
 	struct sfc_flow_list		flow_list;
 
 	unsigned int			rxq_max;
diff --git a/drivers/net/sfc/sfc_flow.c b/drivers/net/sfc/sfc_flow.c
index 0abeabfbf2..a35f20770d 100644
--- a/drivers/net/sfc/sfc_flow.c
+++ b/drivers/net/sfc/sfc_flow.c
@@ -2776,6 +2776,128 @@  sfc_flow_pick_transfer_proxy(struct rte_eth_dev *dev,
 	return 0;
 }
 
+static struct rte_flow_action_handle *
+sfc_flow_action_handle_create(struct rte_eth_dev *dev,
+			      const struct rte_flow_indir_action_conf *conf,
+			      const struct rte_flow_action *action,
+			      struct rte_flow_error *error)
+{
+	struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+	struct rte_flow_action_handle *handle;
+	int ret;
+
+	if (!conf->transfer) {
+		rte_flow_error_set(error, ENOTSUP,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   "non-transfer domain does not support indirect actions");
+		return NULL;
+	}
+
+	if (conf->ingress || conf->egress) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				   NULL, "cannot combine ingress/egress with transfer");
+		return NULL;
+	}
+
+	handle = rte_zmalloc("sfc_rte_flow_action_handle", sizeof(*handle), 0);
+	if (handle == NULL) {
+		rte_flow_error_set(error, ENOMEM,
+				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				   "failed to allocate memory");
+		return NULL;
+	}
+
+	sfc_adapter_lock(sa);
+
+	ret = sfc_mae_indir_action_create(sa, action, handle, error);
+	if (ret != 0) {
+		sfc_adapter_unlock(sa);
+		rte_free(handle);
+		return NULL;
+	}
+
+	TAILQ_INSERT_TAIL(&sa->flow_indir_actions, handle, entries);
+
+	handle->transfer = (bool)conf->transfer;
+
+	sfc_adapter_unlock(sa);
+
+	return handle;
+}
+
+static int
+sfc_flow_action_handle_destroy(struct rte_eth_dev *dev,
+			       struct rte_flow_action_handle *handle,
+			       struct rte_flow_error *error)
+{
+	struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+	struct rte_flow_action_handle *entry;
+	int rc = EINVAL;
+
+	sfc_adapter_lock(sa);
+
+	TAILQ_FOREACH(entry, &sa->flow_indir_actions, entries) {
+		if (entry != handle)
+			continue;
+
+		if (entry->transfer) {
+			rc = sfc_mae_indir_action_destroy(sa, handle,
+							  error);
+			if (rc != 0)
+				goto exit;
+		} else {
+			SFC_ASSERT(B_FALSE);
+		}
+
+		TAILQ_REMOVE(&sa->flow_indir_actions, entry, entries);
+		rte_free(entry);
+		goto exit;
+	}
+
+	rc = rte_flow_error_set(error, ENOENT,
+				RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				"indirect action handle not found");
+
+exit:
+	sfc_adapter_unlock(sa);
+	return rc;
+}
+
+static int
+sfc_flow_action_handle_query(struct rte_eth_dev *dev,
+			     const struct rte_flow_action_handle *handle,
+			     void *data, struct rte_flow_error *error)
+{
+	struct sfc_adapter *sa = sfc_adapter_by_eth_dev(dev);
+	struct rte_flow_action_handle *entry;
+	int rc = EINVAL;
+
+	sfc_adapter_lock(sa);
+
+	TAILQ_FOREACH(entry, &sa->flow_indir_actions, entries) {
+		if (entry != handle)
+			continue;
+
+		if (entry->transfer) {
+			rc = sfc_mae_indir_action_query(sa, handle,
+							data, error);
+		} else {
+			SFC_ASSERT(B_FALSE);
+		}
+
+		goto exit;
+	}
+
+	rc = rte_flow_error_set(error, ENOENT,
+				RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				"indirect action handle not found");
+
+exit:
+	sfc_adapter_unlock(sa);
+	return rc;
+}
+
 const struct rte_flow_ops sfc_flow_ops = {
 	.validate = sfc_flow_validate,
 	.create = sfc_flow_create,
@@ -2783,6 +2905,9 @@  const struct rte_flow_ops sfc_flow_ops = {
 	.flush = sfc_flow_flush,
 	.query = sfc_flow_query,
 	.isolate = sfc_flow_isolate,
+	.action_handle_create = sfc_flow_action_handle_create,
+	.action_handle_destroy = sfc_flow_action_handle_destroy,
+	.action_handle_query = sfc_flow_action_handle_query,
 	.tunnel_decap_set = sfc_ft_decap_set,
 	.tunnel_match = sfc_ft_match,
 	.tunnel_action_decap_release = sfc_ft_action_decap_release,
@@ -2796,6 +2921,7 @@  sfc_flow_init(struct sfc_adapter *sa)
 {
 	SFC_ASSERT(sfc_adapter_is_locked(sa));
 
+	TAILQ_INIT(&sa->flow_indir_actions);
 	TAILQ_INIT(&sa->flow_list);
 }
 
diff --git a/drivers/net/sfc/sfc_flow.h b/drivers/net/sfc/sfc_flow.h
index 8f706fc589..af94d0654a 100644
--- a/drivers/net/sfc/sfc_flow.h
+++ b/drivers/net/sfc/sfc_flow.h
@@ -88,6 +88,20 @@  struct sfc_flow_spec_mae {
 	sfc_mae_conntrack_key_t		ct_key;
 };
 
+/* PMD-specific definition of the opaque type from rte_flow.h */
+struct rte_flow_action_handle {
+	TAILQ_ENTRY(rte_flow_action_handle)	entries;
+
+	bool					transfer;
+	enum rte_flow_action_type		type;
+
+	union {
+		struct sfc_mae_counter		*counter;
+	};
+};
+
+TAILQ_HEAD(sfc_flow_indir_actions, rte_flow_action_handle);
+
 /* Flow specification */
 struct sfc_flow_spec {
 	/* Flow specification type (engine-based) */
diff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c
index e79d88265c..72646afe4d 100644
--- a/drivers/net/sfc/sfc_mae.c
+++ b/drivers/net/sfc/sfc_mae.c
@@ -4005,6 +4005,58 @@  sfc_mae_rule_parse_action_count(struct sfc_adapter *sa,
 	return rc;
 }
 
+static int
+sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,
+				   const struct rte_flow_action_handle *handle,
+				   enum sfc_ft_rule_type ft_rule_type,
+				   struct sfc_mae_aset_ctx *ctx,
+				   struct rte_flow_error *error)
+{
+	struct rte_flow_action_handle *entry;
+	int rc;
+
+	TAILQ_FOREACH(entry, &sa->flow_indir_actions, entries) {
+		if (entry == handle) {
+			sfc_dbg(sa, "attaching to indirect_action=%p", entry);
+
+			switch (entry->type) {
+			case RTE_FLOW_ACTION_TYPE_COUNT:
+				if (ft_rule_type != SFC_FT_RULE_NONE) {
+					return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "cannot use indirect count action in tunnel model");
+				}
+
+				if (ctx->counter != NULL) {
+					return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					  "cannot have multiple actions COUNT in one flow");
+				}
+
+				rc = efx_mae_action_set_populate_count(ctx->spec);
+				if (rc != 0) {
+					return rte_flow_error_set(error, rc,
+					 RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+					 "failed to add COUNT to MAE action set");
+				}
+
+				ctx->counter = entry->counter;
+				++(ctx->counter->refcnt);
+				break;
+			default:
+				SFC_ASSERT(B_FALSE);
+				break;
+			}
+
+			return 0;
+		}
+	}
+
+	return rte_flow_error_set(error, ENOENT,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				  "indirect action handle not found");
+}
+
 static int
 sfc_mae_rule_parse_action_pf_vf(struct sfc_adapter *sa,
 				const struct rte_flow_action_vf *vf_conf,
@@ -4143,6 +4195,7 @@  static const char * const action_names[] = {
 	[RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP] = "OF_SET_VLAN_PCP",
 	[RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP] = "VXLAN_ENCAP",
 	[RTE_FLOW_ACTION_TYPE_COUNT] = "COUNT",
+	[RTE_FLOW_ACTION_TYPE_INDIRECT] = "INDIRECT",
 	[RTE_FLOW_ACTION_TYPE_FLAG] = "FLAG",
 	[RTE_FLOW_ACTION_TYPE_MARK] = "MARK",
 	[RTE_FLOW_ACTION_TYPE_PF] = "PF",
@@ -4258,6 +4311,14 @@  sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 		rc = sfc_mae_rule_parse_action_count(sa, action->conf,
 						     counterp, spec_ptr);
 		break;
+	case RTE_FLOW_ACTION_TYPE_INDIRECT:
+		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_INDIRECT,
+				       bundle->actions_mask);
+		rc = sfc_mae_rule_parse_action_indirect(sa, action->conf,
+							spec_mae->ft_rule_type,
+							ctx, error);
+		custom_error = B_TRUE;
+		break;
 	case RTE_FLOW_ACTION_TYPE_FLAG:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_FLAG,
 				       bundle->actions_mask);
@@ -4813,7 +4874,9 @@  sfc_mae_query_counter(struct sfc_adapter *sa,
 	struct sfc_mae_counter *counter;
 	int rc;
 
-	if (action_rule == NULL || action_rule->action_set->counter == NULL) {
+	if (action_rule == NULL || action_rule->action_set == NULL ||
+	    action_rule->action_set->counter == NULL ||
+	    action_rule->action_set->counter->indirect) {
 		return rte_flow_error_set(error, EINVAL,
 			RTE_FLOW_ERROR_TYPE_ACTION, action,
 			"Queried flow rule does not have count actions");
@@ -4924,3 +4987,105 @@  sfc_mae_switchdev_fini(struct sfc_adapter *sa)
 	sfc_mae_repr_flow_destroy(sa, mae->switchdev_rule_pf_to_ext);
 	sfc_mae_repr_flow_destroy(sa, mae->switchdev_rule_ext_to_pf);
 }
+
+int
+sfc_mae_indir_action_create(struct sfc_adapter *sa,
+			    const struct rte_flow_action *action,
+			    struct rte_flow_action_handle *handle,
+			    struct rte_flow_error *error)
+{
+	int ret;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+	SFC_ASSERT(handle != NULL);
+
+	switch (action->type) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		ret = sfc_mae_rule_parse_action_count(sa, action->conf,
+						      &handle->counter, NULL);
+		if (ret == 0)
+			handle->counter->indirect = true;
+		break;
+	default:
+		ret = ENOTSUP;
+	}
+
+	if (ret != 0) {
+		return rte_flow_error_set(error, ret,
+				RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				"failed to parse indirect action to mae object");
+	}
+
+	handle->type = action->type;
+
+	return 0;
+}
+
+int
+sfc_mae_indir_action_destroy(struct sfc_adapter *sa,
+			     const struct rte_flow_action_handle *handle,
+			     struct rte_flow_error *error)
+{
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+	SFC_ASSERT(handle != NULL);
+
+	switch (handle->type) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		if (handle->counter->refcnt != 1)
+			goto fail;
+
+		sfc_mae_counter_del(sa, handle->counter);
+		break;
+	default:
+		SFC_ASSERT(B_FALSE);
+		break;
+	}
+
+	return 0;
+
+fail:
+	return rte_flow_error_set(error, EIO, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, "indirect action is still in use");
+}
+
+int
+sfc_mae_indir_action_query(struct sfc_adapter *sa,
+			   const struct rte_flow_action_handle *handle,
+			   void *data, struct rte_flow_error *error)
+{
+	int ret;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+	SFC_ASSERT(handle != NULL);
+
+	switch (handle->type) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		SFC_ASSERT(handle->counter != NULL);
+
+		if (handle->counter->fw_rsrc.refcnt == 0)
+			goto fail_not_in_use;
+
+		ret = sfc_mae_counter_get(&sa->mae.counter_registry.counters,
+					  handle->counter, data);
+		if (ret != 0)
+			goto fail_counter_get;
+
+		break;
+	default:
+		goto fail_unsup;
+	}
+
+	return 0;
+
+fail_not_in_use:
+	return rte_flow_error_set(error, EIO, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, "indirect action is not in use");
+
+fail_counter_get:
+	return rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, "failed to collect indirect action COUNT data");
+
+fail_unsup:
+	return rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+				  NULL, "indirect action of this type cannot be queried");
+}
diff --git a/drivers/net/sfc/sfc_mae.h b/drivers/net/sfc/sfc_mae.h
index c73ce0a5e6..e7b7d3a35e 100644
--- a/drivers/net/sfc/sfc_mae.h
+++ b/drivers/net/sfc/sfc_mae.h
@@ -84,6 +84,8 @@  struct sfc_mae_counter {
 	struct sfc_ft_ctx		*ft_ctx;
 
 	struct sfc_mae_fw_rsrc		fw_rsrc;
+
+	bool				indirect;
 };
 
 TAILQ_HEAD(sfc_mae_counters, sfc_mae_counter);
@@ -401,6 +403,19 @@  void sfc_mae_repr_flow_destroy(struct sfc_adapter *sa, struct rte_flow *flow);
 int sfc_mae_switchdev_init(struct sfc_adapter *sa);
 void sfc_mae_switchdev_fini(struct sfc_adapter *sa);
 
+int sfc_mae_indir_action_create(struct sfc_adapter *sa,
+				const struct rte_flow_action *action,
+				struct rte_flow_action_handle *handle,
+				struct rte_flow_error *error);
+
+int sfc_mae_indir_action_destroy(struct sfc_adapter *sa,
+				 const struct rte_flow_action_handle *handle,
+				 struct rte_flow_error *error);
+
+int sfc_mae_indir_action_query(struct sfc_adapter *sa,
+			       const struct rte_flow_action_handle *handle,
+			       void *data, struct rte_flow_error *error);
+
 #ifdef __cplusplus
 }
 #endif