net/mlx5e: Add support to modify hardware flow meter parameters
authorJianbo Liu <jianbol@nvidia.com>
Mon, 7 Jun 2021 03:56:05 +0000 (03:56 +0000)
committerSaeed Mahameed <saeedm@nvidia.com>
Sat, 2 Jul 2022 18:58:27 +0000 (11:58 -0700)
The policing rate and burst from user are converted to flow meter
parameters in hardware. These parameters are set or modified by
ACCESS_ASO WQE, add function to support it.

Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
Reviewed-by: Ariel Levkovich <lariel@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc/meter.h
drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h

index 1643530..f2c0379 100644 (file)
@@ -5,6 +5,22 @@
 #include "en/tc/post_act.h"
 #include "meter.h"
 
+#define MLX5_START_COLOR_SHIFT 28
+#define MLX5_METER_MODE_SHIFT 24
+#define MLX5_CBS_EXP_SHIFT 24
+#define MLX5_CBS_MAN_SHIFT 16
+#define MLX5_CIR_EXP_SHIFT 8
+
+/* cir = 8*(10^9)*cir_mantissa/(2^cir_exponent)) bits/s */
+#define MLX5_CONST_CIR 8000000000ULL
+#define MLX5_CALC_CIR(m, e)  ((MLX5_CONST_CIR * (m)) >> (e))
+#define MLX5_MAX_CIR ((MLX5_CONST_CIR * 0x100) - 1)
+
+/* cbs = cbs_mantissa*2^cbs_exponent */
+#define MLX5_CALC_CBS(m, e)  ((m) << (e))
+#define MLX5_MAX_CBS ((0x100ULL << 0x1F) - 1)
+#define MLX5_MAX_HW_CBS 0x7FFFFFFF
+
 struct mlx5e_flow_meters {
        enum mlx5_flow_namespace_type ns_type;
        struct mlx5_aso *aso;
@@ -16,6 +32,151 @@ struct mlx5e_flow_meters {
        struct mlx5e_post_act *post_act;
 };
 
+static void
+mlx5e_flow_meter_cir_calc(u64 cir, u8 *man, u8 *exp)
+{
+       s64 _cir, _delta, delta = S64_MAX;
+       u8 e, _man = 0, _exp = 0;
+       u64 m;
+
+       for (e = 0; e <= 0x1F; e++) { /* exp width 5bit */
+               m = cir << e;
+               if ((s64)m < 0) /* overflow */
+                       break;
+               m /= MLX5_CONST_CIR;
+               if (m > 0xFF) /* man width 8 bit */
+                       continue;
+               _cir = MLX5_CALC_CIR(m, e);
+               _delta = cir - _cir;
+               if (_delta < delta) {
+                       _man = m;
+                       _exp = e;
+                       if (!_delta)
+                               goto found;
+                       delta = _delta;
+               }
+       }
+
+found:
+       *man = _man;
+       *exp = _exp;
+}
+
+static void
+mlx5e_flow_meter_cbs_calc(u64 cbs, u8 *man, u8 *exp)
+{
+       s64 _cbs, _delta, delta = S64_MAX;
+       u8 e, _man = 0, _exp = 0;
+       u64 m;
+
+       for (e = 0; e <= 0x1F; e++) { /* exp width 5bit */
+               m = cbs >> e;
+               if (m > 0xFF) /* man width 8 bit */
+                       continue;
+               _cbs = MLX5_CALC_CBS(m, e);
+               _delta = cbs - _cbs;
+               if (_delta < delta) {
+                       _man = m;
+                       _exp = e;
+                       if (!_delta)
+                               goto found;
+                       delta = _delta;
+               }
+       }
+
+found:
+       *man = _man;
+       *exp = _exp;
+}
+
+int
+mlx5e_tc_meter_modify(struct mlx5_core_dev *mdev,
+                     struct mlx5e_flow_meter_handle *meter,
+                     struct mlx5e_flow_meter_params *meter_params)
+{
+       struct mlx5_wqe_aso_ctrl_seg *aso_ctrl;
+       struct mlx5_wqe_aso_data_seg *aso_data;
+       struct mlx5e_flow_meters *flow_meters;
+       u8 cir_man, cir_exp, cbs_man, cbs_exp;
+       struct mlx5_aso_wqe *aso_wqe;
+       struct mlx5_aso *aso;
+       u64 rate, burst;
+       u8 ds_cnt;
+       int err;
+
+       rate = meter_params->rate;
+       burst = meter_params->burst;
+
+       /* HW treats each packet as 128 bytes in PPS mode */
+       if (meter_params->mode == MLX5_RATE_LIMIT_PPS) {
+               rate <<= 10;
+               burst <<= 7;
+       }
+
+       if (!rate || rate > MLX5_MAX_CIR || !burst || burst > MLX5_MAX_CBS)
+               return -EINVAL;
+
+       /* HW has limitation of total 31 bits for cbs */
+       if (burst > MLX5_MAX_HW_CBS) {
+               mlx5_core_warn(mdev,
+                              "burst(%lld) is too large, use HW allowed value(%d)\n",
+                              burst, MLX5_MAX_HW_CBS);
+               burst = MLX5_MAX_HW_CBS;
+       }
+
+       mlx5_core_dbg(mdev, "meter mode=%d\n", meter_params->mode);
+       mlx5e_flow_meter_cir_calc(rate, &cir_man, &cir_exp);
+       mlx5_core_dbg(mdev, "rate=%lld, cir=%lld, exp=%d, man=%d\n",
+                     rate, MLX5_CALC_CIR(cir_man, cir_exp), cir_exp, cir_man);
+       mlx5e_flow_meter_cbs_calc(burst, &cbs_man, &cbs_exp);
+       mlx5_core_dbg(mdev, "burst=%lld, cbs=%lld, exp=%d, man=%d\n",
+                     burst, MLX5_CALC_CBS((u64)cbs_man, cbs_exp), cbs_exp, cbs_man);
+
+       if (!cir_man || !cbs_man)
+               return -EINVAL;
+
+       flow_meters = meter->flow_meters;
+       aso = flow_meters->aso;
+
+       mutex_lock(&flow_meters->aso_lock);
+       aso_wqe = mlx5_aso_get_wqe(aso);
+       ds_cnt = DIV_ROUND_UP(sizeof(struct mlx5_aso_wqe_data), MLX5_SEND_WQE_DS);
+       mlx5_aso_build_wqe(aso, ds_cnt, aso_wqe, meter->obj_id,
+                          MLX5_ACCESS_ASO_OPC_MOD_FLOW_METER);
+
+       aso_ctrl = &aso_wqe->aso_ctrl;
+       memset(aso_ctrl, 0, sizeof(*aso_ctrl));
+       aso_ctrl->data_mask_mode = MLX5_ASO_DATA_MASK_MODE_BYTEWISE_64BYTE << 6;
+       aso_ctrl->condition_1_0_operand = MLX5_ASO_ALWAYS_TRUE |
+                                         MLX5_ASO_ALWAYS_TRUE << 4;
+       aso_ctrl->data_offset_condition_operand = MLX5_ASO_LOGICAL_OR << 6;
+       aso_ctrl->data_mask = cpu_to_be64(0x80FFFFFFULL << (meter->idx ? 0 : 32));
+
+       aso_data = (struct mlx5_wqe_aso_data_seg *)(aso_wqe + 1);
+       memset(aso_data, 0, sizeof(*aso_data));
+       aso_data->bytewise_data[meter->idx * 8] = cpu_to_be32((0x1 << 31) | /* valid */
+                                       (MLX5_FLOW_METER_COLOR_GREEN << MLX5_START_COLOR_SHIFT));
+       if (meter_params->mode == MLX5_RATE_LIMIT_PPS)
+               aso_data->bytewise_data[meter->idx * 8] |=
+                       cpu_to_be32(MLX5_FLOW_METER_MODE_NUM_PACKETS << MLX5_METER_MODE_SHIFT);
+       else
+               aso_data->bytewise_data[meter->idx * 8] |=
+                       cpu_to_be32(MLX5_FLOW_METER_MODE_BYTES_IP_LENGTH << MLX5_METER_MODE_SHIFT);
+
+       aso_data->bytewise_data[meter->idx * 8 + 2] = cpu_to_be32((cbs_exp << MLX5_CBS_EXP_SHIFT) |
+                                                                 (cbs_man << MLX5_CBS_MAN_SHIFT) |
+                                                                 (cir_exp << MLX5_CIR_EXP_SHIFT) |
+                                                                 cir_man);
+
+       mlx5_aso_post_wqe(aso, true, &aso_wqe->ctrl);
+
+       /* With newer FW, the wait for the first ASO WQE is more than 2us, put the wait 10ms. */
+       err = mlx5_aso_poll_cq(aso, true, 10);
+       mutex_unlock(&flow_meters->aso_lock);
+
+       return err;
+}
+
 struct mlx5e_flow_meters *
 mlx5e_flow_meters_init(struct mlx5e_priv *priv,
                       enum mlx5_flow_namespace_type ns_type,
index 53dc6c8..0153509 100644 (file)
@@ -6,6 +6,28 @@
 
 struct mlx5e_flow_meters;
 
+enum mlx5e_flow_meter_mode {
+       MLX5_RATE_LIMIT_BPS,
+       MLX5_RATE_LIMIT_PPS,
+};
+
+struct mlx5e_flow_meter_params {
+       enum mlx5e_flow_meter_mode mode;
+       u64 rate;
+       u64 burst;
+};
+
+struct mlx5e_flow_meter_handle {
+       struct mlx5e_flow_meters *flow_meters;
+       u32 obj_id;
+       u8 idx;
+};
+
+int
+mlx5e_tc_meter_modify(struct mlx5_core_dev *mdev,
+                     struct mlx5e_flow_meter_handle *meter,
+                     struct mlx5e_flow_meter_params *meter_params);
+
 struct mlx5e_flow_meters *
 mlx5e_flow_meters_init(struct mlx5e_priv *priv,
                       enum mlx5_flow_namespace_type ns_type,
index 7420df0..b3bbf28 100644 (file)
@@ -45,6 +45,11 @@ struct mlx5_aso_wqe_data {
 };
 
 enum {
+       MLX5_ASO_LOGICAL_AND,
+       MLX5_ASO_LOGICAL_OR,
+};
+
+enum {
        MLX5_ASO_ALWAYS_FALSE,
        MLX5_ASO_ALWAYS_TRUE,
        MLX5_ASO_EQUAL,
@@ -63,6 +68,10 @@ enum {
        MLX5_ASO_DATA_MASK_MODE_CALCULATED_64BYTE,
 };
 
+enum {
+       MLX5_ACCESS_ASO_OPC_MOD_FLOW_METER = 0x2,
+};
+
 struct mlx5_aso;
 
 void *mlx5_aso_get_wqe(struct mlx5_aso *aso);