Indirect count action is useful to applications that
need to gather aggregated statistics for many flows.

Signed-off-by: Ivan Malov <ivan.ma...@arknetworks.am>
Reviewed-by: Andy Moreton <amore...@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(-)

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 6fae4eb0a7..5a77b71d0a 100644
--- a/doc/guides/rel_notes/release_23_07.rst
+++ b/doc/guides/rel_notes/release_23_07.rst
@@ -70,6 +70,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 4d3778eaba..e79df3b56a 100644
--- a/drivers/net/sfc/sfc_mae.c
+++ b/drivers/net/sfc/sfc_mae.c
@@ -4003,6 +4003,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,
@@ -4141,6 +4193,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",
@@ -4256,6 +4309,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);
@@ -4811,7 +4872,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");
@@ -4922,3 +4985,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
-- 
2.30.2

Reply via email to