1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. |
4 | * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> |
5 | */ |
6 | |
7 | #include "devl_internal.h" |
8 | |
9 | struct devlink_sb { |
10 | struct list_head list; |
11 | unsigned int index; |
12 | u32 size; |
13 | u16 ingress_pools_count; |
14 | u16 egress_pools_count; |
15 | u16 ingress_tc_count; |
16 | u16 egress_tc_count; |
17 | }; |
18 | |
19 | static u16 devlink_sb_pool_count(struct devlink_sb *devlink_sb) |
20 | { |
21 | return devlink_sb->ingress_pools_count + devlink_sb->egress_pools_count; |
22 | } |
23 | |
24 | static struct devlink_sb *devlink_sb_get_by_index(struct devlink *devlink, |
25 | unsigned int sb_index) |
26 | { |
27 | struct devlink_sb *devlink_sb; |
28 | |
29 | list_for_each_entry(devlink_sb, &devlink->sb_list, list) { |
30 | if (devlink_sb->index == sb_index) |
31 | return devlink_sb; |
32 | } |
33 | return NULL; |
34 | } |
35 | |
36 | static bool devlink_sb_index_exists(struct devlink *devlink, |
37 | unsigned int sb_index) |
38 | { |
39 | return devlink_sb_get_by_index(devlink, sb_index); |
40 | } |
41 | |
42 | static struct devlink_sb *devlink_sb_get_from_attrs(struct devlink *devlink, |
43 | struct nlattr **attrs) |
44 | { |
45 | if (attrs[DEVLINK_ATTR_SB_INDEX]) { |
46 | u32 sb_index = nla_get_u32(nla: attrs[DEVLINK_ATTR_SB_INDEX]); |
47 | struct devlink_sb *devlink_sb; |
48 | |
49 | devlink_sb = devlink_sb_get_by_index(devlink, sb_index); |
50 | if (!devlink_sb) |
51 | return ERR_PTR(error: -ENODEV); |
52 | return devlink_sb; |
53 | } |
54 | return ERR_PTR(error: -EINVAL); |
55 | } |
56 | |
57 | static struct devlink_sb *devlink_sb_get_from_info(struct devlink *devlink, |
58 | struct genl_info *info) |
59 | { |
60 | return devlink_sb_get_from_attrs(devlink, attrs: info->attrs); |
61 | } |
62 | |
63 | static int devlink_sb_pool_index_get_from_attrs(struct devlink_sb *devlink_sb, |
64 | struct nlattr **attrs, |
65 | u16 *p_pool_index) |
66 | { |
67 | u16 val; |
68 | |
69 | if (!attrs[DEVLINK_ATTR_SB_POOL_INDEX]) |
70 | return -EINVAL; |
71 | |
72 | val = nla_get_u16(nla: attrs[DEVLINK_ATTR_SB_POOL_INDEX]); |
73 | if (val >= devlink_sb_pool_count(devlink_sb)) |
74 | return -EINVAL; |
75 | *p_pool_index = val; |
76 | return 0; |
77 | } |
78 | |
79 | static int devlink_sb_pool_index_get_from_info(struct devlink_sb *devlink_sb, |
80 | struct genl_info *info, |
81 | u16 *p_pool_index) |
82 | { |
83 | return devlink_sb_pool_index_get_from_attrs(devlink_sb, attrs: info->attrs, |
84 | p_pool_index); |
85 | } |
86 | |
87 | static int |
88 | devlink_sb_pool_type_get_from_attrs(struct nlattr **attrs, |
89 | enum devlink_sb_pool_type *p_pool_type) |
90 | { |
91 | u8 val; |
92 | |
93 | if (!attrs[DEVLINK_ATTR_SB_POOL_TYPE]) |
94 | return -EINVAL; |
95 | |
96 | val = nla_get_u8(nla: attrs[DEVLINK_ATTR_SB_POOL_TYPE]); |
97 | if (val != DEVLINK_SB_POOL_TYPE_INGRESS && |
98 | val != DEVLINK_SB_POOL_TYPE_EGRESS) |
99 | return -EINVAL; |
100 | *p_pool_type = val; |
101 | return 0; |
102 | } |
103 | |
104 | static int |
105 | devlink_sb_pool_type_get_from_info(struct genl_info *info, |
106 | enum devlink_sb_pool_type *p_pool_type) |
107 | { |
108 | return devlink_sb_pool_type_get_from_attrs(attrs: info->attrs, p_pool_type); |
109 | } |
110 | |
111 | static int |
112 | devlink_sb_th_type_get_from_attrs(struct nlattr **attrs, |
113 | enum devlink_sb_threshold_type *p_th_type) |
114 | { |
115 | u8 val; |
116 | |
117 | if (!attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]) |
118 | return -EINVAL; |
119 | |
120 | val = nla_get_u8(nla: attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]); |
121 | if (val != DEVLINK_SB_THRESHOLD_TYPE_STATIC && |
122 | val != DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC) |
123 | return -EINVAL; |
124 | *p_th_type = val; |
125 | return 0; |
126 | } |
127 | |
128 | static int |
129 | devlink_sb_th_type_get_from_info(struct genl_info *info, |
130 | enum devlink_sb_threshold_type *p_th_type) |
131 | { |
132 | return devlink_sb_th_type_get_from_attrs(attrs: info->attrs, p_th_type); |
133 | } |
134 | |
135 | static int |
136 | devlink_sb_tc_index_get_from_attrs(struct devlink_sb *devlink_sb, |
137 | struct nlattr **attrs, |
138 | enum devlink_sb_pool_type pool_type, |
139 | u16 *p_tc_index) |
140 | { |
141 | u16 val; |
142 | |
143 | if (!attrs[DEVLINK_ATTR_SB_TC_INDEX]) |
144 | return -EINVAL; |
145 | |
146 | val = nla_get_u16(nla: attrs[DEVLINK_ATTR_SB_TC_INDEX]); |
147 | if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS && |
148 | val >= devlink_sb->ingress_tc_count) |
149 | return -EINVAL; |
150 | if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS && |
151 | val >= devlink_sb->egress_tc_count) |
152 | return -EINVAL; |
153 | *p_tc_index = val; |
154 | return 0; |
155 | } |
156 | |
157 | static int |
158 | devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb, |
159 | struct genl_info *info, |
160 | enum devlink_sb_pool_type pool_type, |
161 | u16 *p_tc_index) |
162 | { |
163 | return devlink_sb_tc_index_get_from_attrs(devlink_sb, attrs: info->attrs, |
164 | pool_type, p_tc_index); |
165 | } |
166 | |
167 | static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink, |
168 | struct devlink_sb *devlink_sb, |
169 | enum devlink_command cmd, u32 portid, |
170 | u32 seq, int flags) |
171 | { |
172 | void *hdr; |
173 | |
174 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
175 | if (!hdr) |
176 | return -EMSGSIZE; |
177 | |
178 | if (devlink_nl_put_handle(msg, devlink)) |
179 | goto nla_put_failure; |
180 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_INDEX, value: devlink_sb->index)) |
181 | goto nla_put_failure; |
182 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_SIZE, value: devlink_sb->size)) |
183 | goto nla_put_failure; |
184 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_INGRESS_POOL_COUNT, |
185 | value: devlink_sb->ingress_pools_count)) |
186 | goto nla_put_failure; |
187 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_EGRESS_POOL_COUNT, |
188 | value: devlink_sb->egress_pools_count)) |
189 | goto nla_put_failure; |
190 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_INGRESS_TC_COUNT, |
191 | value: devlink_sb->ingress_tc_count)) |
192 | goto nla_put_failure; |
193 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_EGRESS_TC_COUNT, |
194 | value: devlink_sb->egress_tc_count)) |
195 | goto nla_put_failure; |
196 | |
197 | genlmsg_end(skb: msg, hdr); |
198 | return 0; |
199 | |
200 | nla_put_failure: |
201 | genlmsg_cancel(skb: msg, hdr); |
202 | return -EMSGSIZE; |
203 | } |
204 | |
205 | int devlink_nl_sb_get_doit(struct sk_buff *skb, struct genl_info *info) |
206 | { |
207 | struct devlink *devlink = info->user_ptr[0]; |
208 | struct devlink_sb *devlink_sb; |
209 | struct sk_buff *msg; |
210 | int err; |
211 | |
212 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
213 | if (IS_ERR(ptr: devlink_sb)) |
214 | return PTR_ERR(ptr: devlink_sb); |
215 | |
216 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
217 | if (!msg) |
218 | return -ENOMEM; |
219 | |
220 | err = devlink_nl_sb_fill(msg, devlink, devlink_sb, |
221 | cmd: DEVLINK_CMD_SB_NEW, |
222 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
223 | if (err) { |
224 | nlmsg_free(skb: msg); |
225 | return err; |
226 | } |
227 | |
228 | return genlmsg_reply(skb: msg, info); |
229 | } |
230 | |
231 | static int |
232 | devlink_nl_sb_get_dump_one(struct sk_buff *msg, struct devlink *devlink, |
233 | struct netlink_callback *cb, int flags) |
234 | { |
235 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
236 | struct devlink_sb *devlink_sb; |
237 | int idx = 0; |
238 | int err = 0; |
239 | |
240 | list_for_each_entry(devlink_sb, &devlink->sb_list, list) { |
241 | if (idx < state->idx) { |
242 | idx++; |
243 | continue; |
244 | } |
245 | err = devlink_nl_sb_fill(msg, devlink, devlink_sb, |
246 | cmd: DEVLINK_CMD_SB_NEW, |
247 | NETLINK_CB(cb->skb).portid, |
248 | seq: cb->nlh->nlmsg_seq, flags); |
249 | if (err) { |
250 | state->idx = idx; |
251 | break; |
252 | } |
253 | idx++; |
254 | } |
255 | |
256 | return err; |
257 | } |
258 | |
259 | int devlink_nl_sb_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) |
260 | { |
261 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_sb_get_dump_one); |
262 | } |
263 | |
264 | static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink, |
265 | struct devlink_sb *devlink_sb, |
266 | u16 pool_index, enum devlink_command cmd, |
267 | u32 portid, u32 seq, int flags) |
268 | { |
269 | struct devlink_sb_pool_info pool_info; |
270 | void *hdr; |
271 | int err; |
272 | |
273 | err = devlink->ops->sb_pool_get(devlink, devlink_sb->index, |
274 | pool_index, &pool_info); |
275 | if (err) |
276 | return err; |
277 | |
278 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
279 | if (!hdr) |
280 | return -EMSGSIZE; |
281 | |
282 | if (devlink_nl_put_handle(msg, devlink)) |
283 | goto nla_put_failure; |
284 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_INDEX, value: devlink_sb->index)) |
285 | goto nla_put_failure; |
286 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_INDEX, value: pool_index)) |
287 | goto nla_put_failure; |
288 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_TYPE, value: pool_info.pool_type)) |
289 | goto nla_put_failure; |
290 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_SIZE, value: pool_info.size)) |
291 | goto nla_put_failure; |
292 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE, |
293 | value: pool_info.threshold_type)) |
294 | goto nla_put_failure; |
295 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_CELL_SIZE, |
296 | value: pool_info.cell_size)) |
297 | goto nla_put_failure; |
298 | |
299 | genlmsg_end(skb: msg, hdr); |
300 | return 0; |
301 | |
302 | nla_put_failure: |
303 | genlmsg_cancel(skb: msg, hdr); |
304 | return -EMSGSIZE; |
305 | } |
306 | |
307 | int devlink_nl_sb_pool_get_doit(struct sk_buff *skb, struct genl_info *info) |
308 | { |
309 | struct devlink *devlink = info->user_ptr[0]; |
310 | struct devlink_sb *devlink_sb; |
311 | struct sk_buff *msg; |
312 | u16 pool_index; |
313 | int err; |
314 | |
315 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
316 | if (IS_ERR(ptr: devlink_sb)) |
317 | return PTR_ERR(ptr: devlink_sb); |
318 | |
319 | err = devlink_sb_pool_index_get_from_info(devlink_sb, info, |
320 | p_pool_index: &pool_index); |
321 | if (err) |
322 | return err; |
323 | |
324 | if (!devlink->ops->sb_pool_get) |
325 | return -EOPNOTSUPP; |
326 | |
327 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
328 | if (!msg) |
329 | return -ENOMEM; |
330 | |
331 | err = devlink_nl_sb_pool_fill(msg, devlink, devlink_sb, pool_index, |
332 | cmd: DEVLINK_CMD_SB_POOL_NEW, |
333 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
334 | if (err) { |
335 | nlmsg_free(skb: msg); |
336 | return err; |
337 | } |
338 | |
339 | return genlmsg_reply(skb: msg, info); |
340 | } |
341 | |
342 | static int __sb_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx, |
343 | struct devlink *devlink, |
344 | struct devlink_sb *devlink_sb, |
345 | u32 portid, u32 seq, int flags) |
346 | { |
347 | u16 pool_count = devlink_sb_pool_count(devlink_sb); |
348 | u16 pool_index; |
349 | int err; |
350 | |
351 | for (pool_index = 0; pool_index < pool_count; pool_index++) { |
352 | if (*p_idx < start) { |
353 | (*p_idx)++; |
354 | continue; |
355 | } |
356 | err = devlink_nl_sb_pool_fill(msg, devlink, |
357 | devlink_sb, |
358 | pool_index, |
359 | cmd: DEVLINK_CMD_SB_POOL_NEW, |
360 | portid, seq, flags); |
361 | if (err) |
362 | return err; |
363 | (*p_idx)++; |
364 | } |
365 | return 0; |
366 | } |
367 | |
368 | static int |
369 | devlink_nl_sb_pool_get_dump_one(struct sk_buff *msg, struct devlink *devlink, |
370 | struct netlink_callback *cb, int flags) |
371 | { |
372 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
373 | struct devlink_sb *devlink_sb; |
374 | int err = 0; |
375 | int idx = 0; |
376 | |
377 | if (!devlink->ops->sb_pool_get) |
378 | return 0; |
379 | |
380 | list_for_each_entry(devlink_sb, &devlink->sb_list, list) { |
381 | err = __sb_pool_get_dumpit(msg, start: state->idx, p_idx: &idx, |
382 | devlink, devlink_sb, |
383 | NETLINK_CB(cb->skb).portid, |
384 | seq: cb->nlh->nlmsg_seq, flags); |
385 | if (err == -EOPNOTSUPP) { |
386 | err = 0; |
387 | } else if (err) { |
388 | state->idx = idx; |
389 | break; |
390 | } |
391 | } |
392 | |
393 | return err; |
394 | } |
395 | |
396 | int devlink_nl_sb_pool_get_dumpit(struct sk_buff *skb, |
397 | struct netlink_callback *cb) |
398 | { |
399 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_sb_pool_get_dump_one); |
400 | } |
401 | |
402 | static int devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index, |
403 | u16 pool_index, u32 size, |
404 | enum devlink_sb_threshold_type threshold_type, |
405 | struct netlink_ext_ack *extack) |
406 | |
407 | { |
408 | const struct devlink_ops *ops = devlink->ops; |
409 | |
410 | if (ops->sb_pool_set) |
411 | return ops->sb_pool_set(devlink, sb_index, pool_index, |
412 | size, threshold_type, extack); |
413 | return -EOPNOTSUPP; |
414 | } |
415 | |
416 | int devlink_nl_sb_pool_set_doit(struct sk_buff *skb, struct genl_info *info) |
417 | { |
418 | struct devlink *devlink = info->user_ptr[0]; |
419 | enum devlink_sb_threshold_type threshold_type; |
420 | struct devlink_sb *devlink_sb; |
421 | u16 pool_index; |
422 | u32 size; |
423 | int err; |
424 | |
425 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
426 | if (IS_ERR(ptr: devlink_sb)) |
427 | return PTR_ERR(ptr: devlink_sb); |
428 | |
429 | err = devlink_sb_pool_index_get_from_info(devlink_sb, info, |
430 | p_pool_index: &pool_index); |
431 | if (err) |
432 | return err; |
433 | |
434 | err = devlink_sb_th_type_get_from_info(info, p_th_type: &threshold_type); |
435 | if (err) |
436 | return err; |
437 | |
438 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_POOL_SIZE)) |
439 | return -EINVAL; |
440 | |
441 | size = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_SB_POOL_SIZE]); |
442 | return devlink_sb_pool_set(devlink, sb_index: devlink_sb->index, |
443 | pool_index, size, threshold_type, |
444 | extack: info->extack); |
445 | } |
446 | |
447 | static int devlink_nl_sb_port_pool_fill(struct sk_buff *msg, |
448 | struct devlink *devlink, |
449 | struct devlink_port *devlink_port, |
450 | struct devlink_sb *devlink_sb, |
451 | u16 pool_index, |
452 | enum devlink_command cmd, |
453 | u32 portid, u32 seq, int flags) |
454 | { |
455 | const struct devlink_ops *ops = devlink->ops; |
456 | u32 threshold; |
457 | void *hdr; |
458 | int err; |
459 | |
460 | err = ops->sb_port_pool_get(devlink_port, devlink_sb->index, |
461 | pool_index, &threshold); |
462 | if (err) |
463 | return err; |
464 | |
465 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
466 | if (!hdr) |
467 | return -EMSGSIZE; |
468 | |
469 | if (devlink_nl_put_handle(msg, devlink)) |
470 | goto nla_put_failure; |
471 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_PORT_INDEX, value: devlink_port->index)) |
472 | goto nla_put_failure; |
473 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_INDEX, value: devlink_sb->index)) |
474 | goto nla_put_failure; |
475 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_INDEX, value: pool_index)) |
476 | goto nla_put_failure; |
477 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_THRESHOLD, value: threshold)) |
478 | goto nla_put_failure; |
479 | |
480 | if (ops->sb_occ_port_pool_get) { |
481 | u32 cur; |
482 | u32 max; |
483 | |
484 | err = ops->sb_occ_port_pool_get(devlink_port, devlink_sb->index, |
485 | pool_index, &cur, &max); |
486 | if (err && err != -EOPNOTSUPP) |
487 | goto sb_occ_get_failure; |
488 | if (!err) { |
489 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_OCC_CUR, value: cur)) |
490 | goto nla_put_failure; |
491 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_OCC_MAX, value: max)) |
492 | goto nla_put_failure; |
493 | } |
494 | } |
495 | |
496 | genlmsg_end(skb: msg, hdr); |
497 | return 0; |
498 | |
499 | nla_put_failure: |
500 | err = -EMSGSIZE; |
501 | sb_occ_get_failure: |
502 | genlmsg_cancel(skb: msg, hdr); |
503 | return err; |
504 | } |
505 | |
506 | int devlink_nl_sb_port_pool_get_doit(struct sk_buff *skb, |
507 | struct genl_info *info) |
508 | { |
509 | struct devlink_port *devlink_port = info->user_ptr[1]; |
510 | struct devlink *devlink = devlink_port->devlink; |
511 | struct devlink_sb *devlink_sb; |
512 | struct sk_buff *msg; |
513 | u16 pool_index; |
514 | int err; |
515 | |
516 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
517 | if (IS_ERR(ptr: devlink_sb)) |
518 | return PTR_ERR(ptr: devlink_sb); |
519 | |
520 | err = devlink_sb_pool_index_get_from_info(devlink_sb, info, |
521 | p_pool_index: &pool_index); |
522 | if (err) |
523 | return err; |
524 | |
525 | if (!devlink->ops->sb_port_pool_get) |
526 | return -EOPNOTSUPP; |
527 | |
528 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
529 | if (!msg) |
530 | return -ENOMEM; |
531 | |
532 | err = devlink_nl_sb_port_pool_fill(msg, devlink, devlink_port, |
533 | devlink_sb, pool_index, |
534 | cmd: DEVLINK_CMD_SB_PORT_POOL_NEW, |
535 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
536 | if (err) { |
537 | nlmsg_free(skb: msg); |
538 | return err; |
539 | } |
540 | |
541 | return genlmsg_reply(skb: msg, info); |
542 | } |
543 | |
544 | static int __sb_port_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx, |
545 | struct devlink *devlink, |
546 | struct devlink_sb *devlink_sb, |
547 | u32 portid, u32 seq, int flags) |
548 | { |
549 | struct devlink_port *devlink_port; |
550 | u16 pool_count = devlink_sb_pool_count(devlink_sb); |
551 | unsigned long port_index; |
552 | u16 pool_index; |
553 | int err; |
554 | |
555 | xa_for_each(&devlink->ports, port_index, devlink_port) { |
556 | for (pool_index = 0; pool_index < pool_count; pool_index++) { |
557 | if (*p_idx < start) { |
558 | (*p_idx)++; |
559 | continue; |
560 | } |
561 | err = devlink_nl_sb_port_pool_fill(msg, devlink, |
562 | devlink_port, |
563 | devlink_sb, |
564 | pool_index, |
565 | cmd: DEVLINK_CMD_SB_PORT_POOL_NEW, |
566 | portid, seq, flags); |
567 | if (err) |
568 | return err; |
569 | (*p_idx)++; |
570 | } |
571 | } |
572 | return 0; |
573 | } |
574 | |
575 | static int |
576 | devlink_nl_sb_port_pool_get_dump_one(struct sk_buff *msg, |
577 | struct devlink *devlink, |
578 | struct netlink_callback *cb, int flags) |
579 | { |
580 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
581 | struct devlink_sb *devlink_sb; |
582 | int idx = 0; |
583 | int err = 0; |
584 | |
585 | if (!devlink->ops->sb_port_pool_get) |
586 | return 0; |
587 | |
588 | list_for_each_entry(devlink_sb, &devlink->sb_list, list) { |
589 | err = __sb_port_pool_get_dumpit(msg, start: state->idx, p_idx: &idx, |
590 | devlink, devlink_sb, |
591 | NETLINK_CB(cb->skb).portid, |
592 | seq: cb->nlh->nlmsg_seq, flags); |
593 | if (err == -EOPNOTSUPP) { |
594 | err = 0; |
595 | } else if (err) { |
596 | state->idx = idx; |
597 | break; |
598 | } |
599 | } |
600 | |
601 | return err; |
602 | } |
603 | |
604 | int devlink_nl_sb_port_pool_get_dumpit(struct sk_buff *skb, |
605 | struct netlink_callback *cb) |
606 | { |
607 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_sb_port_pool_get_dump_one); |
608 | } |
609 | |
610 | static int devlink_sb_port_pool_set(struct devlink_port *devlink_port, |
611 | unsigned int sb_index, u16 pool_index, |
612 | u32 threshold, |
613 | struct netlink_ext_ack *extack) |
614 | |
615 | { |
616 | const struct devlink_ops *ops = devlink_port->devlink->ops; |
617 | |
618 | if (ops->sb_port_pool_set) |
619 | return ops->sb_port_pool_set(devlink_port, sb_index, |
620 | pool_index, threshold, extack); |
621 | return -EOPNOTSUPP; |
622 | } |
623 | |
624 | int devlink_nl_sb_port_pool_set_doit(struct sk_buff *skb, |
625 | struct genl_info *info) |
626 | { |
627 | struct devlink_port *devlink_port = info->user_ptr[1]; |
628 | struct devlink *devlink = info->user_ptr[0]; |
629 | struct devlink_sb *devlink_sb; |
630 | u16 pool_index; |
631 | u32 threshold; |
632 | int err; |
633 | |
634 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
635 | if (IS_ERR(ptr: devlink_sb)) |
636 | return PTR_ERR(ptr: devlink_sb); |
637 | |
638 | err = devlink_sb_pool_index_get_from_info(devlink_sb, info, |
639 | p_pool_index: &pool_index); |
640 | if (err) |
641 | return err; |
642 | |
643 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_THRESHOLD)) |
644 | return -EINVAL; |
645 | |
646 | threshold = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_SB_THRESHOLD]); |
647 | return devlink_sb_port_pool_set(devlink_port, sb_index: devlink_sb->index, |
648 | pool_index, threshold, extack: info->extack); |
649 | } |
650 | |
651 | static int |
652 | devlink_nl_sb_tc_pool_bind_fill(struct sk_buff *msg, struct devlink *devlink, |
653 | struct devlink_port *devlink_port, |
654 | struct devlink_sb *devlink_sb, u16 tc_index, |
655 | enum devlink_sb_pool_type pool_type, |
656 | enum devlink_command cmd, |
657 | u32 portid, u32 seq, int flags) |
658 | { |
659 | const struct devlink_ops *ops = devlink->ops; |
660 | u16 pool_index; |
661 | u32 threshold; |
662 | void *hdr; |
663 | int err; |
664 | |
665 | err = ops->sb_tc_pool_bind_get(devlink_port, devlink_sb->index, |
666 | tc_index, pool_type, |
667 | &pool_index, &threshold); |
668 | if (err) |
669 | return err; |
670 | |
671 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
672 | if (!hdr) |
673 | return -EMSGSIZE; |
674 | |
675 | if (devlink_nl_put_handle(msg, devlink)) |
676 | goto nla_put_failure; |
677 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_PORT_INDEX, value: devlink_port->index)) |
678 | goto nla_put_failure; |
679 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_INDEX, value: devlink_sb->index)) |
680 | goto nla_put_failure; |
681 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_TC_INDEX, value: tc_index)) |
682 | goto nla_put_failure; |
683 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_TYPE, value: pool_type)) |
684 | goto nla_put_failure; |
685 | if (nla_put_u16(skb: msg, attrtype: DEVLINK_ATTR_SB_POOL_INDEX, value: pool_index)) |
686 | goto nla_put_failure; |
687 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_THRESHOLD, value: threshold)) |
688 | goto nla_put_failure; |
689 | |
690 | if (ops->sb_occ_tc_port_bind_get) { |
691 | u32 cur; |
692 | u32 max; |
693 | |
694 | err = ops->sb_occ_tc_port_bind_get(devlink_port, |
695 | devlink_sb->index, |
696 | tc_index, pool_type, |
697 | &cur, &max); |
698 | if (err && err != -EOPNOTSUPP) |
699 | return err; |
700 | if (!err) { |
701 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_OCC_CUR, value: cur)) |
702 | goto nla_put_failure; |
703 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_SB_OCC_MAX, value: max)) |
704 | goto nla_put_failure; |
705 | } |
706 | } |
707 | |
708 | genlmsg_end(skb: msg, hdr); |
709 | return 0; |
710 | |
711 | nla_put_failure: |
712 | genlmsg_cancel(skb: msg, hdr); |
713 | return -EMSGSIZE; |
714 | } |
715 | |
716 | int devlink_nl_sb_tc_pool_bind_get_doit(struct sk_buff *skb, |
717 | struct genl_info *info) |
718 | { |
719 | struct devlink_port *devlink_port = info->user_ptr[1]; |
720 | struct devlink *devlink = devlink_port->devlink; |
721 | struct devlink_sb *devlink_sb; |
722 | struct sk_buff *msg; |
723 | enum devlink_sb_pool_type pool_type; |
724 | u16 tc_index; |
725 | int err; |
726 | |
727 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
728 | if (IS_ERR(ptr: devlink_sb)) |
729 | return PTR_ERR(ptr: devlink_sb); |
730 | |
731 | err = devlink_sb_pool_type_get_from_info(info, p_pool_type: &pool_type); |
732 | if (err) |
733 | return err; |
734 | |
735 | err = devlink_sb_tc_index_get_from_info(devlink_sb, info, |
736 | pool_type, p_tc_index: &tc_index); |
737 | if (err) |
738 | return err; |
739 | |
740 | if (!devlink->ops->sb_tc_pool_bind_get) |
741 | return -EOPNOTSUPP; |
742 | |
743 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
744 | if (!msg) |
745 | return -ENOMEM; |
746 | |
747 | err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink, devlink_port, |
748 | devlink_sb, tc_index, pool_type, |
749 | cmd: DEVLINK_CMD_SB_TC_POOL_BIND_NEW, |
750 | portid: info->snd_portid, |
751 | seq: info->snd_seq, flags: 0); |
752 | if (err) { |
753 | nlmsg_free(skb: msg); |
754 | return err; |
755 | } |
756 | |
757 | return genlmsg_reply(skb: msg, info); |
758 | } |
759 | |
760 | static int __sb_tc_pool_bind_get_dumpit(struct sk_buff *msg, |
761 | int start, int *p_idx, |
762 | struct devlink *devlink, |
763 | struct devlink_sb *devlink_sb, |
764 | u32 portid, u32 seq, int flags) |
765 | { |
766 | struct devlink_port *devlink_port; |
767 | unsigned long port_index; |
768 | u16 tc_index; |
769 | int err; |
770 | |
771 | xa_for_each(&devlink->ports, port_index, devlink_port) { |
772 | for (tc_index = 0; |
773 | tc_index < devlink_sb->ingress_tc_count; tc_index++) { |
774 | if (*p_idx < start) { |
775 | (*p_idx)++; |
776 | continue; |
777 | } |
778 | err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink, |
779 | devlink_port, |
780 | devlink_sb, |
781 | tc_index, |
782 | pool_type: DEVLINK_SB_POOL_TYPE_INGRESS, |
783 | cmd: DEVLINK_CMD_SB_TC_POOL_BIND_NEW, |
784 | portid, seq, |
785 | flags); |
786 | if (err) |
787 | return err; |
788 | (*p_idx)++; |
789 | } |
790 | for (tc_index = 0; |
791 | tc_index < devlink_sb->egress_tc_count; tc_index++) { |
792 | if (*p_idx < start) { |
793 | (*p_idx)++; |
794 | continue; |
795 | } |
796 | err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink, |
797 | devlink_port, |
798 | devlink_sb, |
799 | tc_index, |
800 | pool_type: DEVLINK_SB_POOL_TYPE_EGRESS, |
801 | cmd: DEVLINK_CMD_SB_TC_POOL_BIND_NEW, |
802 | portid, seq, |
803 | flags); |
804 | if (err) |
805 | return err; |
806 | (*p_idx)++; |
807 | } |
808 | } |
809 | return 0; |
810 | } |
811 | |
812 | static int devlink_nl_sb_tc_pool_bind_get_dump_one(struct sk_buff *msg, |
813 | struct devlink *devlink, |
814 | struct netlink_callback *cb, |
815 | int flags) |
816 | { |
817 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
818 | struct devlink_sb *devlink_sb; |
819 | int idx = 0; |
820 | int err = 0; |
821 | |
822 | if (!devlink->ops->sb_tc_pool_bind_get) |
823 | return 0; |
824 | |
825 | list_for_each_entry(devlink_sb, &devlink->sb_list, list) { |
826 | err = __sb_tc_pool_bind_get_dumpit(msg, start: state->idx, p_idx: &idx, |
827 | devlink, devlink_sb, |
828 | NETLINK_CB(cb->skb).portid, |
829 | seq: cb->nlh->nlmsg_seq, flags); |
830 | if (err == -EOPNOTSUPP) { |
831 | err = 0; |
832 | } else if (err) { |
833 | state->idx = idx; |
834 | break; |
835 | } |
836 | } |
837 | |
838 | return err; |
839 | } |
840 | |
841 | int devlink_nl_sb_tc_pool_bind_get_dumpit(struct sk_buff *skb, |
842 | struct netlink_callback *cb) |
843 | { |
844 | return devlink_nl_dumpit(msg: skb, cb, |
845 | dump_one: devlink_nl_sb_tc_pool_bind_get_dump_one); |
846 | } |
847 | |
848 | static int devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port, |
849 | unsigned int sb_index, u16 tc_index, |
850 | enum devlink_sb_pool_type pool_type, |
851 | u16 pool_index, u32 threshold, |
852 | struct netlink_ext_ack *extack) |
853 | |
854 | { |
855 | const struct devlink_ops *ops = devlink_port->devlink->ops; |
856 | |
857 | if (ops->sb_tc_pool_bind_set) |
858 | return ops->sb_tc_pool_bind_set(devlink_port, sb_index, |
859 | tc_index, pool_type, |
860 | pool_index, threshold, extack); |
861 | return -EOPNOTSUPP; |
862 | } |
863 | |
864 | int devlink_nl_sb_tc_pool_bind_set_doit(struct sk_buff *skb, |
865 | struct genl_info *info) |
866 | { |
867 | struct devlink_port *devlink_port = info->user_ptr[1]; |
868 | struct devlink *devlink = info->user_ptr[0]; |
869 | enum devlink_sb_pool_type pool_type; |
870 | struct devlink_sb *devlink_sb; |
871 | u16 tc_index; |
872 | u16 pool_index; |
873 | u32 threshold; |
874 | int err; |
875 | |
876 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
877 | if (IS_ERR(ptr: devlink_sb)) |
878 | return PTR_ERR(ptr: devlink_sb); |
879 | |
880 | err = devlink_sb_pool_type_get_from_info(info, p_pool_type: &pool_type); |
881 | if (err) |
882 | return err; |
883 | |
884 | err = devlink_sb_tc_index_get_from_info(devlink_sb, info, |
885 | pool_type, p_tc_index: &tc_index); |
886 | if (err) |
887 | return err; |
888 | |
889 | err = devlink_sb_pool_index_get_from_info(devlink_sb, info, |
890 | p_pool_index: &pool_index); |
891 | if (err) |
892 | return err; |
893 | |
894 | if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_THRESHOLD)) |
895 | return -EINVAL; |
896 | |
897 | threshold = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_SB_THRESHOLD]); |
898 | return devlink_sb_tc_pool_bind_set(devlink_port, sb_index: devlink_sb->index, |
899 | tc_index, pool_type, |
900 | pool_index, threshold, extack: info->extack); |
901 | } |
902 | |
903 | int devlink_nl_sb_occ_snapshot_doit(struct sk_buff *skb, struct genl_info *info) |
904 | { |
905 | struct devlink *devlink = info->user_ptr[0]; |
906 | const struct devlink_ops *ops = devlink->ops; |
907 | struct devlink_sb *devlink_sb; |
908 | |
909 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
910 | if (IS_ERR(ptr: devlink_sb)) |
911 | return PTR_ERR(ptr: devlink_sb); |
912 | |
913 | if (ops->sb_occ_snapshot) |
914 | return ops->sb_occ_snapshot(devlink, devlink_sb->index); |
915 | return -EOPNOTSUPP; |
916 | } |
917 | |
918 | int devlink_nl_sb_occ_max_clear_doit(struct sk_buff *skb, |
919 | struct genl_info *info) |
920 | { |
921 | struct devlink *devlink = info->user_ptr[0]; |
922 | const struct devlink_ops *ops = devlink->ops; |
923 | struct devlink_sb *devlink_sb; |
924 | |
925 | devlink_sb = devlink_sb_get_from_info(devlink, info); |
926 | if (IS_ERR(ptr: devlink_sb)) |
927 | return PTR_ERR(ptr: devlink_sb); |
928 | |
929 | if (ops->sb_occ_max_clear) |
930 | return ops->sb_occ_max_clear(devlink, devlink_sb->index); |
931 | return -EOPNOTSUPP; |
932 | } |
933 | |
934 | int devl_sb_register(struct devlink *devlink, unsigned int sb_index, |
935 | u32 size, u16 ingress_pools_count, |
936 | u16 egress_pools_count, u16 ingress_tc_count, |
937 | u16 egress_tc_count) |
938 | { |
939 | struct devlink_sb *devlink_sb; |
940 | |
941 | lockdep_assert_held(&devlink->lock); |
942 | |
943 | if (devlink_sb_index_exists(devlink, sb_index)) |
944 | return -EEXIST; |
945 | |
946 | devlink_sb = kzalloc(size: sizeof(*devlink_sb), GFP_KERNEL); |
947 | if (!devlink_sb) |
948 | return -ENOMEM; |
949 | devlink_sb->index = sb_index; |
950 | devlink_sb->size = size; |
951 | devlink_sb->ingress_pools_count = ingress_pools_count; |
952 | devlink_sb->egress_pools_count = egress_pools_count; |
953 | devlink_sb->ingress_tc_count = ingress_tc_count; |
954 | devlink_sb->egress_tc_count = egress_tc_count; |
955 | list_add_tail(new: &devlink_sb->list, head: &devlink->sb_list); |
956 | return 0; |
957 | } |
958 | EXPORT_SYMBOL_GPL(devl_sb_register); |
959 | |
960 | int devlink_sb_register(struct devlink *devlink, unsigned int sb_index, |
961 | u32 size, u16 ingress_pools_count, |
962 | u16 egress_pools_count, u16 ingress_tc_count, |
963 | u16 egress_tc_count) |
964 | { |
965 | int err; |
966 | |
967 | devl_lock(devlink); |
968 | err = devl_sb_register(devlink, sb_index, size, ingress_pools_count, |
969 | egress_pools_count, ingress_tc_count, |
970 | egress_tc_count); |
971 | devl_unlock(devlink); |
972 | return err; |
973 | } |
974 | EXPORT_SYMBOL_GPL(devlink_sb_register); |
975 | |
976 | void devl_sb_unregister(struct devlink *devlink, unsigned int sb_index) |
977 | { |
978 | struct devlink_sb *devlink_sb; |
979 | |
980 | lockdep_assert_held(&devlink->lock); |
981 | |
982 | devlink_sb = devlink_sb_get_by_index(devlink, sb_index); |
983 | WARN_ON(!devlink_sb); |
984 | list_del(entry: &devlink_sb->list); |
985 | kfree(objp: devlink_sb); |
986 | } |
987 | EXPORT_SYMBOL_GPL(devl_sb_unregister); |
988 | |
989 | void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index) |
990 | { |
991 | devl_lock(devlink); |
992 | devl_sb_unregister(devlink, sb_index); |
993 | devl_unlock(devlink); |
994 | } |
995 | EXPORT_SYMBOL_GPL(devlink_sb_unregister); |
996 | |