Initialize the meter object with the TC police mtu parameter.
Use the hardware range destination to compare the pkt len to the mtu setting.
Assign the range destination hit/miss ft to the police conform/exceed
attributes.
Signed-off-by: Oz Shlomo <ozsh@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
 
 #include "act.h"
 #include "en/tc_priv.h"
+#include "fs_core.h"
 
 static bool police_act_validate_control(enum flow_action_id act_id,
                                        struct netlink_ext_ack *extack)
                params->mode = MLX5_RATE_LIMIT_PPS;
                params->rate = act->police.rate_pkt_ps;
                params->burst = act->police.burst_pkt;
+       } else if (act->police.mtu) {
+               params->mtu = act->police.mtu;
        } else {
                return -EOPNOTSUPP;
        }
                    struct mlx5e_priv *priv,
                    struct mlx5_flow_attr *attr)
 {
+       enum mlx5_flow_namespace_type ns =  mlx5e_get_flow_namespace(parse_state->flow);
+       struct mlx5e_flow_meter_params *params = &attr->meter_attr.params;
        int err;
 
-       err = fill_meter_params_from_act(act, &attr->meter_attr.params);
+       err = fill_meter_params_from_act(act, params);
        if (err)
                return err;
 
-       attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO;
-       attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER;
+       if (params->mtu) {
+               if (!(mlx5_fs_get_capabilities(priv->mdev, ns) &
+                     MLX5_FLOW_STEERING_CAP_MATCH_RANGES))
+                       return -EOPNOTSUPP;
+
+               attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+               attr->flags |= MLX5_ATTR_FLAG_MTU;
+       } else {
+               attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO;
+               attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER;
+       }
 
        return 0;
 }
 
 }
 
 static struct mlx5e_flow_meter_handle *
-__mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters)
+__mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters, bool alloc_aso)
 {
        struct mlx5_core_dev *mdev = flow_meters->mdev;
        struct mlx5e_flow_meter_aso_obj *meters_obj;
        }
        meter->act_counter = counter;
 
+       if (!alloc_aso)
+               goto no_aso;
+
        meters_obj = list_first_entry_or_null(&flow_meters->partial_list,
                                              struct mlx5e_flow_meter_aso_obj,
                                              entry);
        }
 
        bitmap_set(meters_obj->meters_map, pos, 1);
-       meter->flow_meters = flow_meters;
        meter->meters_obj = meters_obj;
        meter->obj_id = meters_obj->base_id + pos / 2;
        meter->idx = pos % 2;
 
+no_aso:
+       meter->flow_meters = flow_meters;
        mlx5_core_dbg(mdev, "flow meter allocated, obj_id=0x%x, index=%d\n",
                      meter->obj_id, meter->idx);
 
        mlx5_fc_destroy(mdev, meter->act_counter);
        mlx5_fc_destroy(mdev, meter->drop_counter);
 
+       if (meter->params.mtu)
+               goto out_no_aso;
+
        meters_obj = meter->meters_obj;
        pos = (meter->obj_id - meters_obj->base_id) * 2 + meter->idx;
        bitmap_clear(meters_obj->meters_map, pos, 1);
                list_add(&meters_obj->entry, &flow_meters->partial_list);
        }
 
+out_no_aso:
        mlx5_core_dbg(mdev, "flow meter freed, obj_id=0x%x, index=%d\n",
                      meter->obj_id, meter->idx);
        kfree(meter);
 {
        struct mlx5e_flow_meter_handle *meter;
 
-       meter = __mlx5e_flow_meter_alloc(flow_meters);
+       meter = __mlx5e_flow_meter_alloc(flow_meters, !params->mtu);
        if (IS_ERR(meter))
                return meter;
 
        hash_add(flow_meters->hashtbl, &meter->hlist, params->index);
        meter->params.index = params->index;
+       meter->params.mtu = params->mtu;
        meter->refcnt++;
 
        return meter;
 
        u32 index;
        u64 rate;
        u64 burst;
+       u32 mtu;
 };
 
 struct mlx5e_flow_meter_handle {
 
        return post_meter->rate_steering_table.ft;
 }
 
+struct mlx5_flow_table *
+mlx5e_post_meter_get_mtu_true_ft(struct mlx5e_post_meter_priv *post_meter)
+{
+       return post_meter->mtu_tables.green_table.ft;
+}
+
+struct mlx5_flow_table *
+mlx5e_post_meter_get_mtu_false_ft(struct mlx5e_post_meter_priv *post_meter)
+{
+       return post_meter->mtu_tables.red_table.ft;
+}
+
 static struct mlx5_flow_table *
 mlx5e_post_meter_table_create(struct mlx5e_priv *priv,
                              enum mlx5_flow_namespace_type ns_type)
 
        MLX5E_POST_METER_MTU
 };
 
+#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
+
 struct mlx5_flow_table *
 mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv *post_meter);
 
+struct mlx5_flow_table *
+mlx5e_post_meter_get_mtu_true_ft(struct mlx5e_post_meter_priv *post_meter);
+
+struct mlx5_flow_table *
+mlx5e_post_meter_get_mtu_false_ft(struct mlx5e_post_meter_priv *post_meter);
+
 struct mlx5e_post_meter_priv *
 mlx5e_post_meter_init(struct mlx5e_priv *priv,
                      enum mlx5_flow_namespace_type ns_type,
 void
 mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter);
 
+#else /* CONFIG_MLX5_CLS_ACT */
+
+static inline struct mlx5_flow_table *
+mlx5e_post_meter_get_mtu_true_ft(struct mlx5e_post_meter_priv *post_meter)
+{
+       return NULL;
+}
+
+static inline struct mlx5_flow_table *
+mlx5e_post_meter_get_mtu_false_ft(struct mlx5e_post_meter_priv *post_meter)
+{
+       return NULL;
+}
+
+#endif
+
 #endif /* __MLX5_EN_POST_METER_H__ */
 
 static bool
 is_flow_meter_action(struct mlx5_flow_attr *attr)
 {
-       return ((attr->action & MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO) &&
-               (attr->exe_aso_type == MLX5_EXE_ASO_FLOW_METER));
+       return (((attr->action & MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO) &&
+                (attr->exe_aso_type == MLX5_EXE_ASO_FLOW_METER)) ||
+               attr->flags & MLX5_ATTR_FLAG_MTU);
 }
 
 static int
        struct mlx5e_post_meter_priv *post_meter;
        enum mlx5_flow_namespace_type ns_type;
        struct mlx5e_flow_meter_handle *meter;
+       enum mlx5e_post_meter_type type;
 
        meter = mlx5e_tc_meter_replace(priv->mdev, &attr->meter_attr.params);
        if (IS_ERR(meter)) {
        }
 
        ns_type = mlx5e_tc_meter_get_namespace(meter->flow_meters);
+       type = meter->params.mtu ? MLX5E_POST_METER_MTU : MLX5E_POST_METER_RATE;
        post_meter = mlx5e_post_meter_init(priv, ns_type, post_act,
-                                          MLX5E_POST_METER_RATE,
+                                          type,
                                           meter->act_counter, meter->drop_counter,
                                           attr->branch_true, attr->branch_false);
        if (IS_ERR(post_meter)) {
 
        MLX5_ATTR_FLAG_ACCEPT        = BIT(5),
        MLX5_ATTR_FLAG_CT            = BIT(6),
        MLX5_ATTR_FLAG_TERMINATING   = BIT(7),
+       MLX5_ATTR_FLAG_MTU           = BIT(8),
 };
 
 /* Returns true if any of the flags that require skipping further TC/NF processing are set. */
 
 #include "en/mapping.h"
 #include "devlink.h"
 #include "lag/lag.h"
+#include "en/tc/post_meter.h"
 
 #define mlx5_esw_for_each_rep(esw, i, rep) \
        xa_for_each(&((esw)->offloads.vport_reps), i, rep)
                                         true);
 }
 
+static int
+esw_setup_mtu_dest(struct mlx5_flow_destination *dest,
+                  struct mlx5e_meter_attr *meter,
+                  int i)
+{
+       dest[i].type = MLX5_FLOW_DESTINATION_TYPE_RANGE;
+       dest[i].range.field = MLX5_FLOW_DEST_RANGE_FIELD_PKT_LEN;
+       dest[i].range.min = 0;
+       dest[i].range.max = meter->params.mtu;
+       dest[i].range.hit_ft = mlx5e_post_meter_get_mtu_true_ft(meter->post_meter);
+       dest[i].range.miss_ft = mlx5e_post_meter_get_mtu_false_ft(meter->post_meter);
+
+       return 0;
+}
+
 static int
 esw_setup_sampler_dest(struct mlx5_flow_destination *dest,
                       struct mlx5_flow_act *flow_act,
        } else if (attr->flags & MLX5_ATTR_FLAG_ACCEPT) {
                esw_setup_accept_dest(dest, flow_act, chains, *i);
                (*i)++;
+       } else if (attr->flags & MLX5_ATTR_FLAG_MTU) {
+               err = esw_setup_mtu_dest(dest, &attr->meter_attr, *i);
+               (*i)++;
        } else if (esw_is_indir_table(esw, attr)) {
                err = esw_setup_indir_table(dest, flow_act, esw, attr, spec, true, i);
        } else if (esw_is_chain_src_port_rewrite(esw, esw_attr)) {