1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4 #include <linux/math64.h>
6 #include "en/tc/post_act.h"
8 #include "en/tc_priv.h"
10 #define MLX5_START_COLOR_SHIFT 28
11 #define MLX5_METER_MODE_SHIFT 24
12 #define MLX5_CBS_EXP_SHIFT 24
13 #define MLX5_CBS_MAN_SHIFT 16
14 #define MLX5_CIR_EXP_SHIFT 8
16 /* cir = 8*(10^9)*cir_mantissa/(2^cir_exponent)) bits/s */
17 #define MLX5_CONST_CIR 8000000000ULL
18 #define MLX5_CALC_CIR(m, e) ((MLX5_CONST_CIR * (m)) >> (e))
19 #define MLX5_MAX_CIR ((MLX5_CONST_CIR * 0x100) - 1)
21 /* cbs = cbs_mantissa*2^cbs_exponent */
22 #define MLX5_CALC_CBS(m, e) ((m) << (e))
23 #define MLX5_MAX_CBS ((0x100ULL << 0x1F) - 1)
24 #define MLX5_MAX_HW_CBS 0x7FFFFFFF
26 struct mlx5e_flow_meter_aso_obj {
27 struct list_head entry;
31 unsigned long meters_map[0]; /* must be at the end of this struct */
34 struct mlx5e_flow_meters {
35 enum mlx5_flow_namespace_type ns_type;
37 struct mutex aso_lock; /* Protects aso operations */
41 DECLARE_HASHTABLE(hashtbl, 8);
43 struct mutex sync_lock; /* protect flow meter operations */
44 struct list_head partial_list;
45 struct list_head full_list;
47 struct mlx5_core_dev *mdev;
48 struct mlx5e_post_act *post_act;
52 mlx5e_flow_meter_cir_calc(u64 cir, u8 *man, u8 *exp)
54 s64 _cir, _delta, delta = S64_MAX;
55 u8 e, _man = 0, _exp = 0;
58 for (e = 0; e <= 0x1F; e++) { /* exp width 5bit */
60 if ((s64)m < 0) /* overflow */
62 m = div64_u64(m, MLX5_CONST_CIR);
63 if (m > 0xFF) /* man width 8 bit */
65 _cir = MLX5_CALC_CIR(m, e);
82 mlx5e_flow_meter_cbs_calc(u64 cbs, u8 *man, u8 *exp)
84 s64 _cbs, _delta, delta = S64_MAX;
85 u8 e, _man = 0, _exp = 0;
88 for (e = 0; e <= 0x1F; e++) { /* exp width 5bit */
90 if (m > 0xFF) /* man width 8 bit */
92 _cbs = MLX5_CALC_CBS(m, e);
109 mlx5e_tc_meter_modify(struct mlx5_core_dev *mdev,
110 struct mlx5e_flow_meter_handle *meter,
111 struct mlx5e_flow_meter_params *meter_params)
113 struct mlx5_wqe_aso_ctrl_seg *aso_ctrl;
114 struct mlx5_wqe_aso_data_seg *aso_data;
115 struct mlx5e_flow_meters *flow_meters;
116 u8 cir_man, cir_exp, cbs_man, cbs_exp;
117 struct mlx5_aso_wqe *aso_wqe;
118 struct mlx5_aso *aso;
123 rate = meter_params->rate;
124 burst = meter_params->burst;
126 /* HW treats each packet as 128 bytes in PPS mode */
127 if (meter_params->mode == MLX5_RATE_LIMIT_PPS) {
132 if (!rate || rate > MLX5_MAX_CIR || !burst || burst > MLX5_MAX_CBS)
135 /* HW has limitation of total 31 bits for cbs */
136 if (burst > MLX5_MAX_HW_CBS) {
138 "burst(%lld) is too large, use HW allowed value(%d)\n",
139 burst, MLX5_MAX_HW_CBS);
140 burst = MLX5_MAX_HW_CBS;
143 mlx5_core_dbg(mdev, "meter mode=%d\n", meter_params->mode);
144 mlx5e_flow_meter_cir_calc(rate, &cir_man, &cir_exp);
145 mlx5_core_dbg(mdev, "rate=%lld, cir=%lld, exp=%d, man=%d\n",
146 rate, MLX5_CALC_CIR(cir_man, cir_exp), cir_exp, cir_man);
147 mlx5e_flow_meter_cbs_calc(burst, &cbs_man, &cbs_exp);
148 mlx5_core_dbg(mdev, "burst=%lld, cbs=%lld, exp=%d, man=%d\n",
149 burst, MLX5_CALC_CBS((u64)cbs_man, cbs_exp), cbs_exp, cbs_man);
151 if (!cir_man || !cbs_man)
154 flow_meters = meter->flow_meters;
155 aso = flow_meters->aso;
157 mutex_lock(&flow_meters->aso_lock);
158 aso_wqe = mlx5_aso_get_wqe(aso);
159 ds_cnt = DIV_ROUND_UP(sizeof(struct mlx5_aso_wqe_data), MLX5_SEND_WQE_DS);
160 mlx5_aso_build_wqe(aso, ds_cnt, aso_wqe, meter->obj_id,
161 MLX5_ACCESS_ASO_OPC_MOD_FLOW_METER);
163 aso_ctrl = &aso_wqe->aso_ctrl;
164 memset(aso_ctrl, 0, sizeof(*aso_ctrl));
165 aso_ctrl->data_mask_mode = MLX5_ASO_DATA_MASK_MODE_BYTEWISE_64BYTE << 6;
166 aso_ctrl->condition_1_0_operand = MLX5_ASO_ALWAYS_TRUE |
167 MLX5_ASO_ALWAYS_TRUE << 4;
168 aso_ctrl->data_offset_condition_operand = MLX5_ASO_LOGICAL_OR << 6;
169 aso_ctrl->data_mask = cpu_to_be64(0x80FFFFFFULL << (meter->idx ? 0 : 32));
171 aso_data = (struct mlx5_wqe_aso_data_seg *)(aso_wqe + 1);
172 memset(aso_data, 0, sizeof(*aso_data));
173 aso_data->bytewise_data[meter->idx * 8] = cpu_to_be32((0x1 << 31) | /* valid */
174 (MLX5_FLOW_METER_COLOR_GREEN << MLX5_START_COLOR_SHIFT));
175 if (meter_params->mode == MLX5_RATE_LIMIT_PPS)
176 aso_data->bytewise_data[meter->idx * 8] |=
177 cpu_to_be32(MLX5_FLOW_METER_MODE_NUM_PACKETS << MLX5_METER_MODE_SHIFT);
179 aso_data->bytewise_data[meter->idx * 8] |=
180 cpu_to_be32(MLX5_FLOW_METER_MODE_BYTES_IP_LENGTH << MLX5_METER_MODE_SHIFT);
182 aso_data->bytewise_data[meter->idx * 8 + 2] = cpu_to_be32((cbs_exp << MLX5_CBS_EXP_SHIFT) |
183 (cbs_man << MLX5_CBS_MAN_SHIFT) |
184 (cir_exp << MLX5_CIR_EXP_SHIFT) |
187 mlx5_aso_post_wqe(aso, true, &aso_wqe->ctrl);
189 /* With newer FW, the wait for the first ASO WQE is more than 2us, put the wait 10ms. */
190 err = mlx5_aso_poll_cq(aso, true, 10);
191 mutex_unlock(&flow_meters->aso_lock);
197 mlx5e_flow_meter_create_aso_obj(struct mlx5e_flow_meters *flow_meters, int *obj_id)
199 u32 in[MLX5_ST_SZ_DW(create_flow_meter_aso_obj_in)] = {};
200 u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
201 struct mlx5_core_dev *mdev = flow_meters->mdev;
205 MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
206 MLX5_SET(general_obj_in_cmd_hdr, in, obj_type,
207 MLX5_GENERAL_OBJECT_TYPES_FLOW_METER_ASO);
208 MLX5_SET(general_obj_in_cmd_hdr, in, log_obj_range, flow_meters->log_granularity);
210 obj = MLX5_ADDR_OF(create_flow_meter_aso_obj_in, in, flow_meter_aso_obj);
211 MLX5_SET(flow_meter_aso_obj, obj, meter_aso_access_pd, flow_meters->pdn);
213 err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
215 *obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
216 mlx5_core_dbg(mdev, "flow meter aso obj(0x%x) created\n", *obj_id);
223 mlx5e_flow_meter_destroy_aso_obj(struct mlx5_core_dev *mdev, u32 obj_id)
225 u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
226 u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
228 MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
229 MLX5_SET(general_obj_in_cmd_hdr, in, obj_type,
230 MLX5_GENERAL_OBJECT_TYPES_FLOW_METER_ASO);
231 MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, obj_id);
233 mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
234 mlx5_core_dbg(mdev, "flow meter aso obj(0x%x) destroyed\n", obj_id);
237 static struct mlx5e_flow_meter_handle *
238 __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters)
240 struct mlx5_core_dev *mdev = flow_meters->mdev;
241 struct mlx5e_flow_meter_aso_obj *meters_obj;
242 struct mlx5e_flow_meter_handle *meter;
246 meter = kzalloc(sizeof(*meter), GFP_KERNEL);
248 return ERR_PTR(-ENOMEM);
250 meters_obj = list_first_entry_or_null(&flow_meters->partial_list,
251 struct mlx5e_flow_meter_aso_obj,
253 /* 2 meters in one object */
254 total = 1 << (flow_meters->log_granularity + 1);
256 err = mlx5e_flow_meter_create_aso_obj(flow_meters, &id);
258 mlx5_core_err(mdev, "Failed to create flow meter ASO object\n");
262 meters_obj = kzalloc(sizeof(*meters_obj) + BITS_TO_BYTES(total),
269 meters_obj->base_id = id;
270 meters_obj->total_meters = total;
271 list_add(&meters_obj->entry, &flow_meters->partial_list);
274 pos = find_first_zero_bit(meters_obj->meters_map, total);
275 if (bitmap_weight(meters_obj->meters_map, total) == total - 1) {
276 list_del(&meters_obj->entry);
277 list_add(&meters_obj->entry, &flow_meters->full_list);
281 bitmap_set(meters_obj->meters_map, pos, 1);
282 meter->flow_meters = flow_meters;
283 meter->meters_obj = meters_obj;
284 meter->obj_id = meters_obj->base_id + pos / 2;
285 meter->idx = pos % 2;
287 mlx5_core_dbg(mdev, "flow meter allocated, obj_id=0x%x, index=%d\n",
288 meter->obj_id, meter->idx);
293 mlx5e_flow_meter_destroy_aso_obj(mdev, id);
300 __mlx5e_flow_meter_free(struct mlx5e_flow_meter_handle *meter)
302 struct mlx5e_flow_meters *flow_meters = meter->flow_meters;
303 struct mlx5_core_dev *mdev = flow_meters->mdev;
304 struct mlx5e_flow_meter_aso_obj *meters_obj;
307 meters_obj = meter->meters_obj;
308 pos = (meter->obj_id - meters_obj->base_id) * 2 + meter->idx;
309 bitmap_clear(meters_obj->meters_map, pos, 1);
310 n = bitmap_weight(meters_obj->meters_map, meters_obj->total_meters);
312 list_del(&meters_obj->entry);
313 mlx5e_flow_meter_destroy_aso_obj(mdev, meters_obj->base_id);
315 } else if (n == meters_obj->total_meters - 1) {
316 list_del(&meters_obj->entry);
317 list_add(&meters_obj->entry, &flow_meters->partial_list);
320 mlx5_core_dbg(mdev, "flow meter freed, obj_id=0x%x, index=%d\n",
321 meter->obj_id, meter->idx);
325 struct mlx5e_flow_meter_handle *
326 mlx5e_tc_meter_get(struct mlx5_core_dev *mdev, struct mlx5e_flow_meter_params *params)
328 struct mlx5e_flow_meters *flow_meters;
329 struct mlx5e_flow_meter_handle *meter;
332 flow_meters = mlx5e_get_flow_meters(mdev);
334 return ERR_PTR(-EOPNOTSUPP);
336 mutex_lock(&flow_meters->sync_lock);
337 hash_for_each_possible(flow_meters->hashtbl, meter, hlist, params->index)
338 if (meter->params.index == params->index)
341 meter = __mlx5e_flow_meter_alloc(flow_meters);
343 err = PTR_ERR(meter);
347 hash_add(flow_meters->hashtbl, &meter->hlist, params->index);
348 meter->params.index = params->index;
353 if (meter->params.mode != params->mode || meter->params.rate != params->rate ||
354 meter->params.burst != params->burst) {
355 err = mlx5e_tc_meter_modify(mdev, meter, params);
359 meter->params.mode = params->mode;
360 meter->params.rate = params->rate;
361 meter->params.burst = params->burst;
364 mutex_unlock(&flow_meters->sync_lock);
368 if (--meter->refcnt == 0) {
369 hash_del(&meter->hlist);
370 __mlx5e_flow_meter_free(meter);
373 mutex_unlock(&flow_meters->sync_lock);
378 mlx5e_tc_meter_put(struct mlx5e_flow_meter_handle *meter)
380 struct mlx5e_flow_meters *flow_meters = meter->flow_meters;
382 mutex_lock(&flow_meters->sync_lock);
383 if (--meter->refcnt == 0) {
384 hash_del(&meter->hlist);
385 __mlx5e_flow_meter_free(meter);
387 mutex_unlock(&flow_meters->sync_lock);
390 enum mlx5_flow_namespace_type
391 mlx5e_tc_meter_get_namespace(struct mlx5e_flow_meters *flow_meters)
393 return flow_meters->ns_type;
396 struct mlx5e_flow_meters *
397 mlx5e_flow_meters_init(struct mlx5e_priv *priv,
398 enum mlx5_flow_namespace_type ns_type,
399 struct mlx5e_post_act *post_act)
401 struct mlx5_core_dev *mdev = priv->mdev;
402 struct mlx5e_flow_meters *flow_meters;
405 if (!(MLX5_CAP_GEN_64(mdev, general_obj_types) &
406 MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_FLOW_METER_ASO))
407 return ERR_PTR(-EOPNOTSUPP);
409 if (IS_ERR_OR_NULL(post_act)) {
410 netdev_dbg(priv->netdev,
411 "flow meter offload is not supported, post action is missing\n");
412 return ERR_PTR(-EOPNOTSUPP);
415 flow_meters = kzalloc(sizeof(*flow_meters), GFP_KERNEL);
417 return ERR_PTR(-ENOMEM);
419 err = mlx5_core_alloc_pd(mdev, &flow_meters->pdn);
421 mlx5_core_err(mdev, "Failed to alloc pd for flow meter aso, err=%d\n", err);
425 flow_meters->aso = mlx5_aso_create(mdev, flow_meters->pdn);
426 if (IS_ERR(flow_meters->aso)) {
427 mlx5_core_warn(mdev, "Failed to create aso wqe for flow meter\n");
428 err = PTR_ERR(flow_meters->aso);
432 mutex_init(&flow_meters->sync_lock);
433 INIT_LIST_HEAD(&flow_meters->partial_list);
434 INIT_LIST_HEAD(&flow_meters->full_list);
436 flow_meters->ns_type = ns_type;
437 flow_meters->mdev = mdev;
438 flow_meters->post_act = post_act;
439 mutex_init(&flow_meters->aso_lock);
440 flow_meters->log_granularity = min_t(int, 6,
441 MLX5_CAP_QOS(mdev, log_meter_aso_max_alloc));
446 mlx5_core_dealloc_pd(mdev, flow_meters->pdn);
453 mlx5e_flow_meters_cleanup(struct mlx5e_flow_meters *flow_meters)
455 if (IS_ERR_OR_NULL(flow_meters))
458 mlx5_aso_destroy(flow_meters->aso);
459 mlx5_core_dealloc_pd(flow_meters->mdev, flow_meters->pdn);