Commit 6ddac26c authored by Jianbo Liu's avatar Jianbo Liu Committed by Saeed Mahameed

net/mlx5e: Add support to modify hardware flow meter parameters

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: default avatarJianbo Liu <jianbol@nvidia.com>
Reviewed-by: default avatarRoi Dayan <roid@nvidia.com>
Reviewed-by: default avatarAriel Levkovich <lariel@nvidia.com>
Signed-off-by: default avatarSaeed Mahameed <saeedm@nvidia.com>
parent 74e6b2a8
......@@ -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,
......
......@@ -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,
......
......@@ -44,6 +44,11 @@ struct mlx5_aso_wqe_data {
struct mlx5_wqe_aso_data_seg aso_data;
};
enum {
MLX5_ASO_LOGICAL_AND,
MLX5_ASO_LOGICAL_OR,
};
enum {
MLX5_ASO_ALWAYS_FALSE,
MLX5_ASO_ALWAYS_TRUE,
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment