net/mlx5e: TC, Allocate post meter ft per rule
[platform/kernel/linux-starfive.git] / drivers / net / ethernet / mellanox / mlx5 / core / en / tc / meter.c
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
4 #include <linux/math64.h>
5 #include "lib/aso.h"
6 #include "en/tc/post_act.h"
7 #include "meter.h"
8 #include "en/tc_priv.h"
9
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
15
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)
20
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
25
26 struct mlx5e_flow_meter_aso_obj {
27         struct list_head entry;
28         int base_id;
29         int total_meters;
30
31         unsigned long meters_map[0]; /* must be at the end of this struct */
32 };
33
34 struct mlx5e_flow_meters {
35         enum mlx5_flow_namespace_type ns_type;
36         struct mlx5_aso *aso;
37         struct mutex aso_lock; /* Protects aso operations */
38         int log_granularity;
39         u32 pdn;
40
41         DECLARE_HASHTABLE(hashtbl, 8);
42
43         struct mutex sync_lock; /* protect flow meter operations */
44         struct list_head partial_list;
45         struct list_head full_list;
46
47         struct mlx5_core_dev *mdev;
48         struct mlx5e_post_act *post_act;
49 };
50
51 static void
52 mlx5e_flow_meter_cir_calc(u64 cir, u8 *man, u8 *exp)
53 {
54         s64 _cir, _delta, delta = S64_MAX;
55         u8 e, _man = 0, _exp = 0;
56         u64 m;
57
58         for (e = 0; e <= 0x1F; e++) { /* exp width 5bit */
59                 m = cir << e;
60                 if ((s64)m < 0) /* overflow */
61                         break;
62                 m = div64_u64(m, MLX5_CONST_CIR);
63                 if (m > 0xFF) /* man width 8 bit */
64                         continue;
65                 _cir = MLX5_CALC_CIR(m, e);
66                 _delta = cir - _cir;
67                 if (_delta < delta) {
68                         _man = m;
69                         _exp = e;
70                         if (!_delta)
71                                 goto found;
72                         delta = _delta;
73                 }
74         }
75
76 found:
77         *man = _man;
78         *exp = _exp;
79 }
80
81 static void
82 mlx5e_flow_meter_cbs_calc(u64 cbs, u8 *man, u8 *exp)
83 {
84         s64 _cbs, _delta, delta = S64_MAX;
85         u8 e, _man = 0, _exp = 0;
86         u64 m;
87
88         for (e = 0; e <= 0x1F; e++) { /* exp width 5bit */
89                 m = cbs >> e;
90                 if (m > 0xFF) /* man width 8 bit */
91                         continue;
92                 _cbs = MLX5_CALC_CBS(m, e);
93                 _delta = cbs - _cbs;
94                 if (_delta < delta) {
95                         _man = m;
96                         _exp = e;
97                         if (!_delta)
98                                 goto found;
99                         delta = _delta;
100                 }
101         }
102
103 found:
104         *man = _man;
105         *exp = _exp;
106 }
107
108 int
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)
112 {
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;
119         u64 rate, burst;
120         u8 ds_cnt;
121         int err;
122
123         rate = meter_params->rate;
124         burst = meter_params->burst;
125
126         /* HW treats each packet as 128 bytes in PPS mode */
127         if (meter_params->mode == MLX5_RATE_LIMIT_PPS) {
128                 rate <<= 10;
129                 burst <<= 7;
130         }
131
132         if (!rate || rate > MLX5_MAX_CIR || !burst || burst > MLX5_MAX_CBS)
133                 return -EINVAL;
134
135         /* HW has limitation of total 31 bits for cbs */
136         if (burst > MLX5_MAX_HW_CBS) {
137                 mlx5_core_warn(mdev,
138                                "burst(%lld) is too large, use HW allowed value(%d)\n",
139                                burst, MLX5_MAX_HW_CBS);
140                 burst = MLX5_MAX_HW_CBS;
141         }
142
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);
150
151         if (!cir_man || !cbs_man)
152                 return -EINVAL;
153
154         flow_meters = meter->flow_meters;
155         aso = flow_meters->aso;
156
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);
162
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));
170
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);
178         else
179                 aso_data->bytewise_data[meter->idx * 8] |=
180                         cpu_to_be32(MLX5_FLOW_METER_MODE_BYTES_IP_LENGTH << MLX5_METER_MODE_SHIFT);
181
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) |
185                                                                   cir_man);
186
187         mlx5_aso_post_wqe(aso, true, &aso_wqe->ctrl);
188
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);
192
193         return err;
194 }
195
196 static int
197 mlx5e_flow_meter_create_aso_obj(struct mlx5e_flow_meters *flow_meters, int *obj_id)
198 {
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;
202         void *obj;
203         int err;
204
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);
209
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);
212
213         err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
214         if (!err) {
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);
217         }
218
219         return err;
220 }
221
222 static void
223 mlx5e_flow_meter_destroy_aso_obj(struct mlx5_core_dev *mdev, u32 obj_id)
224 {
225         u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
226         u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
227
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);
232
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);
235 }
236
237 static struct mlx5e_flow_meter_handle *
238 __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters)
239 {
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;
243         int err, pos, total;
244         u32 id;
245
246         meter = kzalloc(sizeof(*meter), GFP_KERNEL);
247         if (!meter)
248                 return ERR_PTR(-ENOMEM);
249
250         meters_obj = list_first_entry_or_null(&flow_meters->partial_list,
251                                               struct mlx5e_flow_meter_aso_obj,
252                                               entry);
253         /* 2 meters in one object */
254         total = 1 << (flow_meters->log_granularity + 1);
255         if (!meters_obj) {
256                 err = mlx5e_flow_meter_create_aso_obj(flow_meters, &id);
257                 if (err) {
258                         mlx5_core_err(mdev, "Failed to create flow meter ASO object\n");
259                         goto err_create;
260                 }
261
262                 meters_obj = kzalloc(sizeof(*meters_obj) + BITS_TO_BYTES(total),
263                                      GFP_KERNEL);
264                 if (!meters_obj) {
265                         err = -ENOMEM;
266                         goto err_mem;
267                 }
268
269                 meters_obj->base_id = id;
270                 meters_obj->total_meters = total;
271                 list_add(&meters_obj->entry, &flow_meters->partial_list);
272                 pos = 0;
273         } else {
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);
278                 }
279         }
280
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;
286
287         mlx5_core_dbg(mdev, "flow meter allocated, obj_id=0x%x, index=%d\n",
288                       meter->obj_id, meter->idx);
289
290         return meter;
291
292 err_mem:
293         mlx5e_flow_meter_destroy_aso_obj(mdev, id);
294 err_create:
295         kfree(meter);
296         return ERR_PTR(err);
297 }
298
299 static void
300 __mlx5e_flow_meter_free(struct mlx5e_flow_meter_handle *meter)
301 {
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;
305         int n, pos;
306
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);
311         if (n == 0) {
312                 list_del(&meters_obj->entry);
313                 mlx5e_flow_meter_destroy_aso_obj(mdev, meters_obj->base_id);
314                 kfree(meters_obj);
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);
318         }
319
320         mlx5_core_dbg(mdev, "flow meter freed, obj_id=0x%x, index=%d\n",
321                       meter->obj_id, meter->idx);
322         kfree(meter);
323 }
324
325 struct mlx5e_flow_meter_handle *
326 mlx5e_tc_meter_get(struct mlx5_core_dev *mdev, struct mlx5e_flow_meter_params *params)
327 {
328         struct mlx5e_flow_meters *flow_meters;
329         struct mlx5e_flow_meter_handle *meter;
330         int err;
331
332         flow_meters = mlx5e_get_flow_meters(mdev);
333         if (!flow_meters)
334                 return ERR_PTR(-EOPNOTSUPP);
335
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)
339                         goto add_ref;
340
341         meter = __mlx5e_flow_meter_alloc(flow_meters);
342         if (IS_ERR(meter)) {
343                 err = PTR_ERR(meter);
344                 goto err_alloc;
345         }
346
347         hash_add(flow_meters->hashtbl, &meter->hlist, params->index);
348         meter->params.index = params->index;
349
350 add_ref:
351         meter->refcnt++;
352
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);
356                 if (err)
357                         goto err_update;
358
359                 meter->params.mode = params->mode;
360                 meter->params.rate = params->rate;
361                 meter->params.burst = params->burst;
362         }
363
364         mutex_unlock(&flow_meters->sync_lock);
365         return meter;
366
367 err_update:
368         if (--meter->refcnt == 0) {
369                 hash_del(&meter->hlist);
370                 __mlx5e_flow_meter_free(meter);
371         }
372 err_alloc:
373         mutex_unlock(&flow_meters->sync_lock);
374         return ERR_PTR(err);
375 }
376
377 void
378 mlx5e_tc_meter_put(struct mlx5e_flow_meter_handle *meter)
379 {
380         struct mlx5e_flow_meters *flow_meters = meter->flow_meters;
381
382         mutex_lock(&flow_meters->sync_lock);
383         if (--meter->refcnt == 0) {
384                 hash_del(&meter->hlist);
385                 __mlx5e_flow_meter_free(meter);
386         }
387         mutex_unlock(&flow_meters->sync_lock);
388 }
389
390 enum mlx5_flow_namespace_type
391 mlx5e_tc_meter_get_namespace(struct mlx5e_flow_meters *flow_meters)
392 {
393         return flow_meters->ns_type;
394 }
395
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)
400 {
401         struct mlx5_core_dev *mdev = priv->mdev;
402         struct mlx5e_flow_meters *flow_meters;
403         int err;
404
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);
408
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);
413         }
414
415         flow_meters = kzalloc(sizeof(*flow_meters), GFP_KERNEL);
416         if (!flow_meters)
417                 return ERR_PTR(-ENOMEM);
418
419         err = mlx5_core_alloc_pd(mdev, &flow_meters->pdn);
420         if (err) {
421                 mlx5_core_err(mdev, "Failed to alloc pd for flow meter aso, err=%d\n", err);
422                 goto err_out;
423         }
424
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);
429                 goto err_sq;
430         }
431
432         mutex_init(&flow_meters->sync_lock);
433         INIT_LIST_HEAD(&flow_meters->partial_list);
434         INIT_LIST_HEAD(&flow_meters->full_list);
435
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));
442
443         return flow_meters;
444
445 err_sq:
446         mlx5_core_dealloc_pd(mdev, flow_meters->pdn);
447 err_out:
448         kfree(flow_meters);
449         return ERR_PTR(err);
450 }
451
452 void
453 mlx5e_flow_meters_cleanup(struct mlx5e_flow_meters *flow_meters)
454 {
455         if (IS_ERR_OR_NULL(flow_meters))
456                 return;
457
458         mlx5_aso_destroy(flow_meters->aso);
459         mlx5_core_dealloc_pd(flow_meters->mdev, flow_meters->pdn);
460         kfree(flow_meters);
461 }