net/mlx5e: TC, Support tc action api for police
authorRoi Dayan <roid@nvidia.com>
Thu, 21 Jul 2022 08:04:51 +0000 (11:04 +0300)
committerSaeed Mahameed <saeedm@nvidia.com>
Thu, 28 Jul 2022 20:55:27 +0000 (13:55 -0700)
Add support for tc action api for police.
Offloading standalone police action without
a tc rule and reporting stats.

Signed-off-by: Roi Dayan <roid@nvidia.com>
Reviewed-by: Jianbo Liu <jianbol@nvidia.com>
Reviewed-by: Oz Shlomo <ozsh@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/act.h
drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h

index 86fa0bd..fac7e3f 100644 (file)
@@ -21,6 +21,7 @@
 #include "en/tc/sample.h"
 #include "en_accel/ipsec_rxtx.h"
 #include "en/tc/int_port.h"
+#include "en/tc/act/act.h"
 
 struct mlx5e_rep_indr_block_priv {
        struct net_device *netdev;
@@ -511,6 +512,120 @@ mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch,
        return 0;
 }
 
+static int
+mlx5e_rep_indr_replace_act(struct mlx5e_rep_priv *rpriv,
+                          struct flow_offload_action *fl_act)
+
+{
+       struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       enum mlx5_flow_namespace_type ns_type;
+       struct flow_action_entry *action;
+       struct mlx5e_tc_act *act;
+       bool add = false;
+       int i;
+
+       /* There is no use case currently for more than one action (e.g. pedit).
+        * when there will be, need to handle cleaning multiple actions on err.
+        */
+       if (!flow_offload_has_one_action(&fl_act->action))
+               return -EOPNOTSUPP;
+
+       if (esw && esw->mode == MLX5_ESWITCH_OFFLOADS)
+               ns_type = MLX5_FLOW_NAMESPACE_FDB;
+       else
+               ns_type = MLX5_FLOW_NAMESPACE_KERNEL;
+
+       flow_action_for_each(i, action, &fl_act->action) {
+               act = mlx5e_tc_act_get(action->id, ns_type);
+               if (!act)
+                       continue;
+
+               if (!act->offload_action)
+                       continue;
+
+               if (!act->offload_action(priv, fl_act, action))
+                       add = true;
+       }
+
+       return add ? 0 : -EOPNOTSUPP;
+}
+
+static int
+mlx5e_rep_indr_destroy_act(struct mlx5e_rep_priv *rpriv,
+                          struct flow_offload_action *fl_act)
+{
+       struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       enum mlx5_flow_namespace_type ns_type;
+       struct mlx5e_tc_act *act;
+
+       if (esw && esw->mode == MLX5_ESWITCH_OFFLOADS)
+               ns_type = MLX5_FLOW_NAMESPACE_FDB;
+       else
+               ns_type = MLX5_FLOW_NAMESPACE_KERNEL;
+
+       act = mlx5e_tc_act_get(fl_act->id, ns_type);
+       if (!act || !act->destroy_action)
+               return -EOPNOTSUPP;
+
+       return act->destroy_action(priv, fl_act);
+}
+
+static int
+mlx5e_rep_indr_stats_act(struct mlx5e_rep_priv *rpriv,
+                        struct flow_offload_action *fl_act)
+
+{
+       struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       enum mlx5_flow_namespace_type ns_type;
+       struct mlx5e_tc_act *act;
+
+       if (esw && esw->mode == MLX5_ESWITCH_OFFLOADS)
+               ns_type = MLX5_FLOW_NAMESPACE_FDB;
+       else
+               ns_type = MLX5_FLOW_NAMESPACE_KERNEL;
+
+       act = mlx5e_tc_act_get(fl_act->id, ns_type);
+       if (!act || !act->stats_action)
+               return -EOPNOTSUPP;
+
+       return act->stats_action(priv, fl_act);
+}
+
+static int
+mlx5e_rep_indr_setup_act(struct mlx5e_rep_priv *rpriv,
+                        struct flow_offload_action *fl_act)
+{
+       switch (fl_act->command) {
+       case FLOW_ACT_REPLACE:
+               return mlx5e_rep_indr_replace_act(rpriv, fl_act);
+       case FLOW_ACT_DESTROY:
+               return mlx5e_rep_indr_destroy_act(rpriv, fl_act);
+       case FLOW_ACT_STATS:
+               return mlx5e_rep_indr_stats_act(rpriv, fl_act);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int
+mlx5e_rep_indr_no_dev_setup(struct mlx5e_rep_priv *rpriv,
+                           enum tc_setup_type type,
+                           void *data)
+{
+       if (!data)
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case TC_SETUP_ACT:
+               return mlx5e_rep_indr_setup_act(rpriv, data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static
 int mlx5e_rep_indr_setup_cb(struct net_device *netdev, struct Qdisc *sch, void *cb_priv,
                            enum tc_setup_type type, void *type_data,
@@ -518,7 +633,7 @@ int mlx5e_rep_indr_setup_cb(struct net_device *netdev, struct Qdisc *sch, void *
                            void (*cleanup)(struct flow_block_cb *block_cb))
 {
        if (!netdev)
-               return -EOPNOTSUPP;
+               return mlx5e_rep_indr_no_dev_setup(cb_priv, type, data);
 
        switch (type) {
        case TC_SETUP_BLOCK:
index 095ff8e..e1570ff 100644 (file)
@@ -50,6 +50,16 @@ struct mlx5e_tc_act {
        bool (*is_multi_table_act)(struct mlx5e_priv *priv,
                                   const struct flow_action_entry *act,
                                   struct mlx5_flow_attr *attr);
+
+       int (*offload_action)(struct mlx5e_priv *priv,
+                             struct flow_offload_action *fl_act,
+                             struct flow_action_entry *act);
+
+       int (*destroy_action)(struct mlx5e_priv *priv,
+                             struct flow_offload_action *fl_act);
+
+       int (*stats_action)(struct mlx5e_priv *priv,
+                           struct flow_offload_action *fl_act);
 };
 
 struct mlx5e_tc_flow_action {
index 4bd9c04..3752235 100644 (file)
@@ -24,14 +24,9 @@ tc_act_can_offload_police(struct mlx5e_tc_act_parse_state *parse_state,
 }
 
 static int
-tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state,
-                   const struct flow_action_entry *act,
-                   struct mlx5e_priv *priv,
-                   struct mlx5_flow_attr *attr)
+fill_meter_params_from_act(const struct flow_action_entry *act,
+                          struct mlx5e_flow_meter_params *params)
 {
-       struct mlx5e_flow_meter_params *params;
-
-       params = &attr->meter_attr.params;
        params->index = act->hw_index;
        if (act->police.rate_bytes_ps) {
                params->mode = MLX5_RATE_LIMIT_BPS;
@@ -46,6 +41,21 @@ tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state,
                return -EOPNOTSUPP;
        }
 
+       return 0;
+}
+
+static int
+tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state,
+                   const struct flow_action_entry *act,
+                   struct mlx5e_priv *priv,
+                   struct mlx5_flow_attr *attr)
+{
+       int err;
+
+       err = fill_meter_params_from_act(act, &attr->meter_attr.params);
+       if (err)
+               return err;
+
        attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO;
        attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER;
 
@@ -60,8 +70,84 @@ tc_act_is_multi_table_act_police(struct mlx5e_priv *priv,
        return true;
 }
 
+static int
+tc_act_police_offload(struct mlx5e_priv *priv,
+                     struct flow_offload_action *fl_act,
+                     struct flow_action_entry *act)
+{
+       struct mlx5e_flow_meter_params params = {};
+       struct mlx5e_flow_meter_handle *meter;
+       int err = 0;
+
+       err = fill_meter_params_from_act(act, &params);
+       if (err)
+               return err;
+
+       meter = mlx5e_tc_meter_get(priv->mdev, &params);
+       if (IS_ERR(meter) && PTR_ERR(meter) == -ENOENT) {
+               meter = mlx5e_tc_meter_replace(priv->mdev, &params);
+       } else if (!IS_ERR(meter)) {
+               err = mlx5e_tc_meter_update(meter, &params);
+               mlx5e_tc_meter_put(meter);
+       }
+
+       if (IS_ERR(meter)) {
+               NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
+               mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
+               err = PTR_ERR(meter);
+       }
+
+       return err;
+}
+
+static int
+tc_act_police_destroy(struct mlx5e_priv *priv,
+                     struct flow_offload_action *fl_act)
+{
+       struct mlx5e_flow_meter_params params = {};
+       struct mlx5e_flow_meter_handle *meter;
+
+       params.index = fl_act->index;
+       meter = mlx5e_tc_meter_get(priv->mdev, &params);
+       if (IS_ERR(meter)) {
+               NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
+               mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
+               return PTR_ERR(meter);
+       }
+       /* first put for the get and second for cleanup */
+       mlx5e_tc_meter_put(meter);
+       mlx5e_tc_meter_put(meter);
+       return 0;
+}
+
+static int
+tc_act_police_stats(struct mlx5e_priv *priv,
+                   struct flow_offload_action *fl_act)
+{
+       struct mlx5e_flow_meter_params params = {};
+       struct mlx5e_flow_meter_handle *meter;
+       u64 bytes, packets, drops, lastuse;
+
+       params.index = fl_act->index;
+       meter = mlx5e_tc_meter_get(priv->mdev, &params);
+       if (IS_ERR(meter)) {
+               NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
+               mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
+               return PTR_ERR(meter);
+       }
+
+       mlx5e_tc_meter_get_stats(meter, &bytes, &packets, &drops, &lastuse);
+       flow_stats_update(&fl_act->stats, bytes, packets, drops, lastuse,
+                         FLOW_ACTION_HW_STATS_DELAYED);
+       mlx5e_tc_meter_put(meter);
+       return 0;
+}
+
 struct mlx5e_tc_act mlx5e_tc_act_police = {
        .can_offload = tc_act_can_offload_police,
        .parse_action = tc_act_parse_police,
        .is_multi_table_act = tc_act_is_multi_table_act_police,
+       .offload_action = tc_act_police_offload,
+       .destroy_action = tc_act_police_destroy,
+       .stats_action = tc_act_police_stats,
 };
index 17529cc..a53e205 100644 (file)
@@ -561,3 +561,19 @@ mlx5e_flow_meters_cleanup(struct mlx5e_flow_meters *flow_meters)
        mlx5_core_dealloc_pd(flow_meters->mdev, flow_meters->pdn);
        kfree(flow_meters);
 }
+
+void
+mlx5e_tc_meter_get_stats(struct mlx5e_flow_meter_handle *meter,
+                        u64 *bytes, u64 *packets, u64 *drops, u64 *lastuse)
+{
+       u64 bytes1, packets1, lastuse1;
+       u64 bytes2, packets2, lastuse2;
+
+       mlx5_fc_query_cached(meter->green_counter, &bytes1, &packets1, &lastuse1);
+       mlx5_fc_query_cached(meter->red_counter, &bytes2, &packets2, &lastuse2);
+
+       *bytes = bytes1 + bytes2;
+       *packets = packets1 + packets2;
+       *drops = packets2;
+       *lastuse = max_t(u64, lastuse1, lastuse2);
+}
index 71ffa86..6de6e8a 100644 (file)
@@ -67,4 +67,8 @@ mlx5e_flow_meters_init(struct mlx5e_priv *priv,
 void
 mlx5e_flow_meters_cleanup(struct mlx5e_flow_meters *flow_meters);
 
+void
+mlx5e_tc_meter_get_stats(struct mlx5e_flow_meter_handle *meter,
+                        u64 *bytes, u64 *packets, u64 *drops, u64 *lastuse);
+
 #endif /* __MLX5_EN_FLOW_METER_H__ */