1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2020 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #include <linux/idr.h> |
5 | #include <linux/log2.h> |
6 | #include <linux/mutex.h> |
7 | #include <linux/netlink.h> |
8 | #include <net/devlink.h> |
9 | |
10 | #include "spectrum.h" |
11 | |
12 | struct mlxsw_sp_policer_family { |
13 | enum mlxsw_sp_policer_type type; |
14 | enum mlxsw_reg_qpcr_g qpcr_type; |
15 | struct mlxsw_sp *mlxsw_sp; |
16 | u16 start_index; /* Inclusive */ |
17 | u16 end_index; /* Exclusive */ |
18 | struct idr policer_idr; |
19 | struct mutex lock; /* Protects policer_idr */ |
20 | atomic_t policers_count; |
21 | const struct mlxsw_sp_policer_family_ops *ops; |
22 | }; |
23 | |
24 | struct mlxsw_sp_policer { |
25 | struct mlxsw_sp_policer_params params; |
26 | u16 index; |
27 | }; |
28 | |
29 | struct mlxsw_sp_policer_family_ops { |
30 | int (*init)(struct mlxsw_sp_policer_family *family); |
31 | void (*fini)(struct mlxsw_sp_policer_family *family); |
32 | int (*policer_index_alloc)(struct mlxsw_sp_policer_family *family, |
33 | struct mlxsw_sp_policer *policer); |
34 | struct mlxsw_sp_policer * (*policer_index_free)(struct mlxsw_sp_policer_family *family, |
35 | u16 policer_index); |
36 | int (*policer_init)(struct mlxsw_sp_policer_family *family, |
37 | const struct mlxsw_sp_policer *policer); |
38 | int (*policer_params_check)(const struct mlxsw_sp_policer_family *family, |
39 | const struct mlxsw_sp_policer_params *params, |
40 | struct netlink_ext_ack *extack); |
41 | }; |
42 | |
43 | struct mlxsw_sp_policer_core { |
44 | struct mlxsw_sp_policer_family *family_arr[MLXSW_SP_POLICER_TYPE_MAX + 1]; |
45 | const struct mlxsw_sp_policer_core_ops *ops; |
46 | u8 lowest_bs_bits; |
47 | u8 highest_bs_bits; |
48 | }; |
49 | |
50 | struct mlxsw_sp_policer_core_ops { |
51 | int (*init)(struct mlxsw_sp_policer_core *policer_core); |
52 | }; |
53 | |
54 | static u64 mlxsw_sp_policer_rate_bytes_ps_kbps(u64 rate_bytes_ps) |
55 | { |
56 | return div_u64(dividend: rate_bytes_ps, divisor: 1000) * BITS_PER_BYTE; |
57 | } |
58 | |
59 | static u8 mlxsw_sp_policer_burst_bytes_hw_units(u64 burst_bytes) |
60 | { |
61 | /* Provided burst size is in bytes. The ASIC burst size value is |
62 | * (2 ^ bs) * 512 bits. Convert the provided size to 512-bit units. |
63 | */ |
64 | u64 bs512 = div_u64(dividend: burst_bytes, divisor: 64); |
65 | |
66 | if (!bs512) |
67 | return 0; |
68 | |
69 | return fls64(x: bs512) - 1; |
70 | } |
71 | |
72 | static u64 mlxsw_sp_policer_single_rate_occ_get(void *priv) |
73 | { |
74 | struct mlxsw_sp_policer_family *family = priv; |
75 | |
76 | return atomic_read(v: &family->policers_count); |
77 | } |
78 | |
79 | static int |
80 | mlxsw_sp_policer_single_rate_family_init(struct mlxsw_sp_policer_family *family) |
81 | { |
82 | struct mlxsw_core *core = family->mlxsw_sp->core; |
83 | struct devlink *devlink; |
84 | |
85 | /* CPU policers are allocated from the first N policers in the global |
86 | * range, so skip them. |
87 | */ |
88 | if (!MLXSW_CORE_RES_VALID(core, MAX_GLOBAL_POLICERS) || |
89 | !MLXSW_CORE_RES_VALID(core, MAX_CPU_POLICERS)) |
90 | return -EIO; |
91 | |
92 | family->start_index = MLXSW_CORE_RES_GET(core, MAX_CPU_POLICERS); |
93 | family->end_index = MLXSW_CORE_RES_GET(core, MAX_GLOBAL_POLICERS); |
94 | |
95 | atomic_set(v: &family->policers_count, i: 0); |
96 | devlink = priv_to_devlink(priv: core); |
97 | devl_resource_occ_get_register(devlink, |
98 | resource_id: MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS, |
99 | occ_get: mlxsw_sp_policer_single_rate_occ_get, |
100 | occ_get_priv: family); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static void |
106 | mlxsw_sp_policer_single_rate_family_fini(struct mlxsw_sp_policer_family *family) |
107 | { |
108 | struct devlink *devlink = priv_to_devlink(priv: family->mlxsw_sp->core); |
109 | |
110 | devl_resource_occ_get_unregister(devlink, |
111 | resource_id: MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS); |
112 | WARN_ON(atomic_read(&family->policers_count) != 0); |
113 | } |
114 | |
115 | static int |
116 | mlxsw_sp_policer_single_rate_index_alloc(struct mlxsw_sp_policer_family *family, |
117 | struct mlxsw_sp_policer *policer) |
118 | { |
119 | int id; |
120 | |
121 | mutex_lock(&family->lock); |
122 | id = idr_alloc(&family->policer_idr, ptr: policer, start: family->start_index, |
123 | end: family->end_index, GFP_KERNEL); |
124 | mutex_unlock(lock: &family->lock); |
125 | |
126 | if (id < 0) |
127 | return id; |
128 | |
129 | atomic_inc(v: &family->policers_count); |
130 | policer->index = id; |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static struct mlxsw_sp_policer * |
136 | mlxsw_sp_policer_single_rate_index_free(struct mlxsw_sp_policer_family *family, |
137 | u16 policer_index) |
138 | { |
139 | struct mlxsw_sp_policer *policer; |
140 | |
141 | atomic_dec(v: &family->policers_count); |
142 | |
143 | mutex_lock(&family->lock); |
144 | policer = idr_remove(&family->policer_idr, id: policer_index); |
145 | mutex_unlock(lock: &family->lock); |
146 | |
147 | WARN_ON(!policer); |
148 | |
149 | return policer; |
150 | } |
151 | |
152 | static int |
153 | mlxsw_sp_policer_single_rate_init(struct mlxsw_sp_policer_family *family, |
154 | const struct mlxsw_sp_policer *policer) |
155 | { |
156 | u64 rate_kbps = mlxsw_sp_policer_rate_bytes_ps_kbps(rate_bytes_ps: policer->params.rate); |
157 | u8 bs = mlxsw_sp_policer_burst_bytes_hw_units(burst_bytes: policer->params.burst); |
158 | struct mlxsw_sp *mlxsw_sp = family->mlxsw_sp; |
159 | char qpcr_pl[MLXSW_REG_QPCR_LEN]; |
160 | |
161 | mlxsw_reg_qpcr_pack(payload: qpcr_pl, pid: policer->index, ir_units: MLXSW_REG_QPCR_IR_UNITS_K, |
162 | bytes: true, cir: rate_kbps, cbs: bs); |
163 | mlxsw_reg_qpcr_clear_counter_set(buf: qpcr_pl, val: true); |
164 | |
165 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(qpcr), payload: qpcr_pl); |
166 | } |
167 | |
168 | static int |
169 | mlxsw_sp_policer_single_rate_params_check(const struct mlxsw_sp_policer_family *family, |
170 | const struct mlxsw_sp_policer_params *params, |
171 | struct netlink_ext_ack *extack) |
172 | { |
173 | struct mlxsw_sp_policer_core *policer_core = family->mlxsw_sp->policer_core; |
174 | u64 rate_bps = params->rate * BITS_PER_BYTE; |
175 | u8 bs; |
176 | |
177 | if (!params->bytes) { |
178 | NL_SET_ERR_MSG_MOD(extack, "Only bandwidth policing is currently supported by single rate policers" ); |
179 | return -EINVAL; |
180 | } |
181 | |
182 | if (!is_power_of_2(n: params->burst)) { |
183 | NL_SET_ERR_MSG_MOD(extack, "Policer burst size is not power of two" ); |
184 | return -EINVAL; |
185 | } |
186 | |
187 | bs = mlxsw_sp_policer_burst_bytes_hw_units(burst_bytes: params->burst); |
188 | |
189 | if (bs < policer_core->lowest_bs_bits) { |
190 | NL_SET_ERR_MSG_MOD(extack, "Policer burst size lower than limit" ); |
191 | return -EINVAL; |
192 | } |
193 | |
194 | if (bs > policer_core->highest_bs_bits) { |
195 | NL_SET_ERR_MSG_MOD(extack, "Policer burst size higher than limit" ); |
196 | return -EINVAL; |
197 | } |
198 | |
199 | if (rate_bps < MLXSW_REG_QPCR_LOWEST_CIR_BITS) { |
200 | NL_SET_ERR_MSG_MOD(extack, "Policer rate lower than limit" ); |
201 | return -EINVAL; |
202 | } |
203 | |
204 | if (rate_bps > MLXSW_REG_QPCR_HIGHEST_CIR_BITS) { |
205 | NL_SET_ERR_MSG_MOD(extack, "Policer rate higher than limit" ); |
206 | return -EINVAL; |
207 | } |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static const struct mlxsw_sp_policer_family_ops mlxsw_sp_policer_single_rate_ops = { |
213 | .init = mlxsw_sp_policer_single_rate_family_init, |
214 | .fini = mlxsw_sp_policer_single_rate_family_fini, |
215 | .policer_index_alloc = mlxsw_sp_policer_single_rate_index_alloc, |
216 | .policer_index_free = mlxsw_sp_policer_single_rate_index_free, |
217 | .policer_init = mlxsw_sp_policer_single_rate_init, |
218 | .policer_params_check = mlxsw_sp_policer_single_rate_params_check, |
219 | }; |
220 | |
221 | static const struct mlxsw_sp_policer_family mlxsw_sp_policer_single_rate_family = { |
222 | .type = MLXSW_SP_POLICER_TYPE_SINGLE_RATE, |
223 | .qpcr_type = MLXSW_REG_QPCR_G_GLOBAL, |
224 | .ops = &mlxsw_sp_policer_single_rate_ops, |
225 | }; |
226 | |
227 | static const struct mlxsw_sp_policer_family *mlxsw_sp_policer_family_arr[] = { |
228 | [MLXSW_SP_POLICER_TYPE_SINGLE_RATE] = &mlxsw_sp_policer_single_rate_family, |
229 | }; |
230 | |
231 | int mlxsw_sp_policer_add(struct mlxsw_sp *mlxsw_sp, |
232 | enum mlxsw_sp_policer_type type, |
233 | const struct mlxsw_sp_policer_params *params, |
234 | struct netlink_ext_ack *extack, u16 *p_policer_index) |
235 | { |
236 | struct mlxsw_sp_policer_family *family; |
237 | struct mlxsw_sp_policer *policer; |
238 | int err; |
239 | |
240 | family = mlxsw_sp->policer_core->family_arr[type]; |
241 | |
242 | err = family->ops->policer_params_check(family, params, extack); |
243 | if (err) |
244 | return err; |
245 | |
246 | policer = kmalloc(size: sizeof(*policer), GFP_KERNEL); |
247 | if (!policer) |
248 | return -ENOMEM; |
249 | policer->params = *params; |
250 | |
251 | err = family->ops->policer_index_alloc(family, policer); |
252 | if (err) { |
253 | NL_SET_ERR_MSG_MOD(extack, "Failed to allocate policer index" ); |
254 | goto err_policer_index_alloc; |
255 | } |
256 | |
257 | err = family->ops->policer_init(family, policer); |
258 | if (err) { |
259 | NL_SET_ERR_MSG_MOD(extack, "Failed to initialize policer" ); |
260 | goto err_policer_init; |
261 | } |
262 | |
263 | *p_policer_index = policer->index; |
264 | |
265 | return 0; |
266 | |
267 | err_policer_init: |
268 | family->ops->policer_index_free(family, policer->index); |
269 | err_policer_index_alloc: |
270 | kfree(objp: policer); |
271 | return err; |
272 | } |
273 | |
274 | void mlxsw_sp_policer_del(struct mlxsw_sp *mlxsw_sp, |
275 | enum mlxsw_sp_policer_type type, u16 policer_index) |
276 | { |
277 | struct mlxsw_sp_policer_family *family; |
278 | struct mlxsw_sp_policer *policer; |
279 | |
280 | family = mlxsw_sp->policer_core->family_arr[type]; |
281 | policer = family->ops->policer_index_free(family, policer_index); |
282 | kfree(objp: policer); |
283 | } |
284 | |
285 | int mlxsw_sp_policer_drops_counter_get(struct mlxsw_sp *mlxsw_sp, |
286 | enum mlxsw_sp_policer_type type, |
287 | u16 policer_index, u64 *p_drops) |
288 | { |
289 | struct mlxsw_sp_policer_family *family; |
290 | char qpcr_pl[MLXSW_REG_QPCR_LEN]; |
291 | int err; |
292 | |
293 | family = mlxsw_sp->policer_core->family_arr[type]; |
294 | |
295 | MLXSW_REG_ZERO(qpcr, qpcr_pl); |
296 | mlxsw_reg_qpcr_pid_set(buf: qpcr_pl, val: policer_index); |
297 | mlxsw_reg_qpcr_g_set(buf: qpcr_pl, val: family->qpcr_type); |
298 | err = mlxsw_reg_query(mlxsw_core: mlxsw_sp->core, MLXSW_REG(qpcr), payload: qpcr_pl); |
299 | if (err) |
300 | return err; |
301 | |
302 | *p_drops = mlxsw_reg_qpcr_violate_count_get(buf: qpcr_pl); |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | static int |
308 | mlxsw_sp_policer_family_register(struct mlxsw_sp *mlxsw_sp, |
309 | const struct mlxsw_sp_policer_family *tmpl) |
310 | { |
311 | struct mlxsw_sp_policer_family *family; |
312 | int err; |
313 | |
314 | family = kmemdup(p: tmpl, size: sizeof(*family), GFP_KERNEL); |
315 | if (!family) |
316 | return -ENOMEM; |
317 | |
318 | family->mlxsw_sp = mlxsw_sp; |
319 | idr_init(idr: &family->policer_idr); |
320 | mutex_init(&family->lock); |
321 | |
322 | err = family->ops->init(family); |
323 | if (err) |
324 | goto err_family_init; |
325 | |
326 | if (WARN_ON(family->start_index >= family->end_index)) { |
327 | err = -EINVAL; |
328 | goto err_index_check; |
329 | } |
330 | |
331 | mlxsw_sp->policer_core->family_arr[tmpl->type] = family; |
332 | |
333 | return 0; |
334 | |
335 | err_index_check: |
336 | family->ops->fini(family); |
337 | err_family_init: |
338 | mutex_destroy(lock: &family->lock); |
339 | idr_destroy(&family->policer_idr); |
340 | kfree(objp: family); |
341 | return err; |
342 | } |
343 | |
344 | static void |
345 | mlxsw_sp_policer_family_unregister(struct mlxsw_sp *mlxsw_sp, |
346 | struct mlxsw_sp_policer_family *family) |
347 | { |
348 | family->ops->fini(family); |
349 | mutex_destroy(lock: &family->lock); |
350 | WARN_ON(!idr_is_empty(&family->policer_idr)); |
351 | idr_destroy(&family->policer_idr); |
352 | kfree(objp: family); |
353 | } |
354 | |
355 | int mlxsw_sp_policers_init(struct mlxsw_sp *mlxsw_sp) |
356 | { |
357 | struct mlxsw_sp_policer_core *policer_core; |
358 | int i, err; |
359 | |
360 | policer_core = kzalloc(size: sizeof(*policer_core), GFP_KERNEL); |
361 | if (!policer_core) |
362 | return -ENOMEM; |
363 | mlxsw_sp->policer_core = policer_core; |
364 | policer_core->ops = mlxsw_sp->policer_core_ops; |
365 | |
366 | err = policer_core->ops->init(policer_core); |
367 | if (err) |
368 | goto err_init; |
369 | |
370 | for (i = 0; i < MLXSW_SP_POLICER_TYPE_MAX + 1; i++) { |
371 | err = mlxsw_sp_policer_family_register(mlxsw_sp, tmpl: mlxsw_sp_policer_family_arr[i]); |
372 | if (err) |
373 | goto err_family_register; |
374 | } |
375 | |
376 | return 0; |
377 | |
378 | err_family_register: |
379 | for (i--; i >= 0; i--) { |
380 | struct mlxsw_sp_policer_family *family; |
381 | |
382 | family = mlxsw_sp->policer_core->family_arr[i]; |
383 | mlxsw_sp_policer_family_unregister(mlxsw_sp, family); |
384 | } |
385 | err_init: |
386 | kfree(objp: mlxsw_sp->policer_core); |
387 | return err; |
388 | } |
389 | |
390 | void mlxsw_sp_policers_fini(struct mlxsw_sp *mlxsw_sp) |
391 | { |
392 | int i; |
393 | |
394 | for (i = MLXSW_SP_POLICER_TYPE_MAX; i >= 0; i--) { |
395 | struct mlxsw_sp_policer_family *family; |
396 | |
397 | family = mlxsw_sp->policer_core->family_arr[i]; |
398 | mlxsw_sp_policer_family_unregister(mlxsw_sp, family); |
399 | } |
400 | |
401 | kfree(objp: mlxsw_sp->policer_core); |
402 | } |
403 | |
404 | int mlxsw_sp_policer_resources_register(struct mlxsw_core *mlxsw_core) |
405 | { |
406 | u64 global_policers, cpu_policers, single_rate_policers; |
407 | struct devlink *devlink = priv_to_devlink(priv: mlxsw_core); |
408 | struct devlink_resource_size_params size_params; |
409 | int err; |
410 | |
411 | if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_GLOBAL_POLICERS) || |
412 | !MLXSW_CORE_RES_VALID(mlxsw_core, MAX_CPU_POLICERS)) |
413 | return -EIO; |
414 | |
415 | global_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_GLOBAL_POLICERS); |
416 | cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS); |
417 | single_rate_policers = global_policers - cpu_policers; |
418 | |
419 | devlink_resource_size_params_init(size_params: &size_params, size_min: global_policers, |
420 | size_max: global_policers, size_granularity: 1, |
421 | unit: DEVLINK_RESOURCE_UNIT_ENTRY); |
422 | err = devl_resource_register(devlink, resource_name: "global_policers" , |
423 | resource_size: global_policers, |
424 | resource_id: MLXSW_SP_RESOURCE_GLOBAL_POLICERS, |
425 | DEVLINK_RESOURCE_ID_PARENT_TOP, |
426 | size_params: &size_params); |
427 | if (err) |
428 | return err; |
429 | |
430 | devlink_resource_size_params_init(size_params: &size_params, size_min: single_rate_policers, |
431 | size_max: single_rate_policers, size_granularity: 1, |
432 | unit: DEVLINK_RESOURCE_UNIT_ENTRY); |
433 | err = devl_resource_register(devlink, resource_name: "single_rate_policers" , |
434 | resource_size: single_rate_policers, |
435 | resource_id: MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS, |
436 | parent_resource_id: MLXSW_SP_RESOURCE_GLOBAL_POLICERS, |
437 | size_params: &size_params); |
438 | if (err) |
439 | return err; |
440 | |
441 | return 0; |
442 | } |
443 | |
444 | static int |
445 | mlxsw_sp1_policer_core_init(struct mlxsw_sp_policer_core *policer_core) |
446 | { |
447 | policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP1; |
448 | policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP1; |
449 | |
450 | return 0; |
451 | } |
452 | |
453 | const struct mlxsw_sp_policer_core_ops mlxsw_sp1_policer_core_ops = { |
454 | .init = mlxsw_sp1_policer_core_init, |
455 | }; |
456 | |
457 | static int |
458 | mlxsw_sp2_policer_core_init(struct mlxsw_sp_policer_core *policer_core) |
459 | { |
460 | policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP2; |
461 | policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP2; |
462 | |
463 | return 0; |
464 | } |
465 | |
466 | const struct mlxsw_sp_policer_core_ops mlxsw_sp2_policer_core_ops = { |
467 | .init = mlxsw_sp2_policer_core_init, |
468 | }; |
469 | |