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 <trace/events/devlink.h> |
8 | |
9 | #include "devl_internal.h" |
10 | |
11 | struct devlink_stats { |
12 | u64_stats_t rx_bytes; |
13 | u64_stats_t rx_packets; |
14 | struct u64_stats_sync syncp; |
15 | }; |
16 | |
17 | /** |
18 | * struct devlink_trap_policer_item - Packet trap policer attributes. |
19 | * @policer: Immutable packet trap policer attributes. |
20 | * @rate: Rate in packets / sec. |
21 | * @burst: Burst size in packets. |
22 | * @list: trap_policer_list member. |
23 | * |
24 | * Describes packet trap policer attributes. Created by devlink during trap |
25 | * policer registration. |
26 | */ |
27 | struct devlink_trap_policer_item { |
28 | const struct devlink_trap_policer *policer; |
29 | u64 rate; |
30 | u64 burst; |
31 | struct list_head list; |
32 | }; |
33 | |
34 | /** |
35 | * struct devlink_trap_group_item - Packet trap group attributes. |
36 | * @group: Immutable packet trap group attributes. |
37 | * @policer_item: Associated policer item. Can be NULL. |
38 | * @list: trap_group_list member. |
39 | * @stats: Trap group statistics. |
40 | * |
41 | * Describes packet trap group attributes. Created by devlink during trap |
42 | * group registration. |
43 | */ |
44 | struct devlink_trap_group_item { |
45 | const struct devlink_trap_group *group; |
46 | struct devlink_trap_policer_item *policer_item; |
47 | struct list_head list; |
48 | struct devlink_stats __percpu *stats; |
49 | }; |
50 | |
51 | /** |
52 | * struct devlink_trap_item - Packet trap attributes. |
53 | * @trap: Immutable packet trap attributes. |
54 | * @group_item: Associated group item. |
55 | * @list: trap_list member. |
56 | * @action: Trap action. |
57 | * @stats: Trap statistics. |
58 | * @priv: Driver private information. |
59 | * |
60 | * Describes both mutable and immutable packet trap attributes. Created by |
61 | * devlink during trap registration and used for all trap related operations. |
62 | */ |
63 | struct devlink_trap_item { |
64 | const struct devlink_trap *trap; |
65 | struct devlink_trap_group_item *group_item; |
66 | struct list_head list; |
67 | enum devlink_trap_action action; |
68 | struct devlink_stats __percpu *stats; |
69 | void *priv; |
70 | }; |
71 | |
72 | static struct devlink_trap_policer_item * |
73 | devlink_trap_policer_item_lookup(struct devlink *devlink, u32 id) |
74 | { |
75 | struct devlink_trap_policer_item *policer_item; |
76 | |
77 | list_for_each_entry(policer_item, &devlink->trap_policer_list, list) { |
78 | if (policer_item->policer->id == id) |
79 | return policer_item; |
80 | } |
81 | |
82 | return NULL; |
83 | } |
84 | |
85 | static struct devlink_trap_item * |
86 | devlink_trap_item_lookup(struct devlink *devlink, const char *name) |
87 | { |
88 | struct devlink_trap_item *trap_item; |
89 | |
90 | list_for_each_entry(trap_item, &devlink->trap_list, list) { |
91 | if (!strcmp(trap_item->trap->name, name)) |
92 | return trap_item; |
93 | } |
94 | |
95 | return NULL; |
96 | } |
97 | |
98 | static struct devlink_trap_item * |
99 | devlink_trap_item_get_from_info(struct devlink *devlink, |
100 | struct genl_info *info) |
101 | { |
102 | struct nlattr *attr; |
103 | |
104 | if (!info->attrs[DEVLINK_ATTR_TRAP_NAME]) |
105 | return NULL; |
106 | attr = info->attrs[DEVLINK_ATTR_TRAP_NAME]; |
107 | |
108 | return devlink_trap_item_lookup(devlink, name: nla_data(nla: attr)); |
109 | } |
110 | |
111 | static int |
112 | devlink_trap_action_get_from_info(struct genl_info *info, |
113 | enum devlink_trap_action *p_trap_action) |
114 | { |
115 | u8 val; |
116 | |
117 | val = nla_get_u8(nla: info->attrs[DEVLINK_ATTR_TRAP_ACTION]); |
118 | switch (val) { |
119 | case DEVLINK_TRAP_ACTION_DROP: |
120 | case DEVLINK_TRAP_ACTION_TRAP: |
121 | case DEVLINK_TRAP_ACTION_MIRROR: |
122 | *p_trap_action = val; |
123 | break; |
124 | default: |
125 | return -EINVAL; |
126 | } |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | static int devlink_trap_metadata_put(struct sk_buff *msg, |
132 | const struct devlink_trap *trap) |
133 | { |
134 | struct nlattr *attr; |
135 | |
136 | attr = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_TRAP_METADATA); |
137 | if (!attr) |
138 | return -EMSGSIZE; |
139 | |
140 | if ((trap->metadata_cap & DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT) && |
141 | nla_put_flag(skb: msg, attrtype: DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT)) |
142 | goto nla_put_failure; |
143 | if ((trap->metadata_cap & DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE) && |
144 | nla_put_flag(skb: msg, attrtype: DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE)) |
145 | goto nla_put_failure; |
146 | |
147 | nla_nest_end(skb: msg, start: attr); |
148 | |
149 | return 0; |
150 | |
151 | nla_put_failure: |
152 | nla_nest_cancel(skb: msg, start: attr); |
153 | return -EMSGSIZE; |
154 | } |
155 | |
156 | static void devlink_trap_stats_read(struct devlink_stats __percpu *trap_stats, |
157 | struct devlink_stats *stats) |
158 | { |
159 | int i; |
160 | |
161 | memset(stats, 0, sizeof(*stats)); |
162 | for_each_possible_cpu(i) { |
163 | struct devlink_stats *cpu_stats; |
164 | u64 rx_packets, rx_bytes; |
165 | unsigned int start; |
166 | |
167 | cpu_stats = per_cpu_ptr(trap_stats, i); |
168 | do { |
169 | start = u64_stats_fetch_begin(syncp: &cpu_stats->syncp); |
170 | rx_packets = u64_stats_read(p: &cpu_stats->rx_packets); |
171 | rx_bytes = u64_stats_read(p: &cpu_stats->rx_bytes); |
172 | } while (u64_stats_fetch_retry(syncp: &cpu_stats->syncp, start)); |
173 | |
174 | u64_stats_add(p: &stats->rx_packets, val: rx_packets); |
175 | u64_stats_add(p: &stats->rx_bytes, val: rx_bytes); |
176 | } |
177 | } |
178 | |
179 | static int |
180 | devlink_trap_group_stats_put(struct sk_buff *msg, |
181 | struct devlink_stats __percpu *trap_stats) |
182 | { |
183 | struct devlink_stats stats; |
184 | struct nlattr *attr; |
185 | |
186 | devlink_trap_stats_read(trap_stats, stats: &stats); |
187 | |
188 | attr = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_STATS); |
189 | if (!attr) |
190 | return -EMSGSIZE; |
191 | |
192 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_STATS_RX_PACKETS, |
193 | value: u64_stats_read(p: &stats.rx_packets), |
194 | padattr: DEVLINK_ATTR_PAD)) |
195 | goto nla_put_failure; |
196 | |
197 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_STATS_RX_BYTES, |
198 | value: u64_stats_read(p: &stats.rx_bytes), |
199 | padattr: DEVLINK_ATTR_PAD)) |
200 | goto nla_put_failure; |
201 | |
202 | nla_nest_end(skb: msg, start: attr); |
203 | |
204 | return 0; |
205 | |
206 | nla_put_failure: |
207 | nla_nest_cancel(skb: msg, start: attr); |
208 | return -EMSGSIZE; |
209 | } |
210 | |
211 | static int devlink_trap_stats_put(struct sk_buff *msg, struct devlink *devlink, |
212 | const struct devlink_trap_item *trap_item) |
213 | { |
214 | struct devlink_stats stats; |
215 | struct nlattr *attr; |
216 | u64 drops = 0; |
217 | int err; |
218 | |
219 | if (devlink->ops->trap_drop_counter_get) { |
220 | err = devlink->ops->trap_drop_counter_get(devlink, |
221 | trap_item->trap, |
222 | &drops); |
223 | if (err) |
224 | return err; |
225 | } |
226 | |
227 | devlink_trap_stats_read(trap_stats: trap_item->stats, stats: &stats); |
228 | |
229 | attr = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_STATS); |
230 | if (!attr) |
231 | return -EMSGSIZE; |
232 | |
233 | if (devlink->ops->trap_drop_counter_get && |
234 | nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_STATS_RX_DROPPED, value: drops, |
235 | padattr: DEVLINK_ATTR_PAD)) |
236 | goto nla_put_failure; |
237 | |
238 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_STATS_RX_PACKETS, |
239 | value: u64_stats_read(p: &stats.rx_packets), |
240 | padattr: DEVLINK_ATTR_PAD)) |
241 | goto nla_put_failure; |
242 | |
243 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_STATS_RX_BYTES, |
244 | value: u64_stats_read(p: &stats.rx_bytes), |
245 | padattr: DEVLINK_ATTR_PAD)) |
246 | goto nla_put_failure; |
247 | |
248 | nla_nest_end(skb: msg, start: attr); |
249 | |
250 | return 0; |
251 | |
252 | nla_put_failure: |
253 | nla_nest_cancel(skb: msg, start: attr); |
254 | return -EMSGSIZE; |
255 | } |
256 | |
257 | static int devlink_nl_trap_fill(struct sk_buff *msg, struct devlink *devlink, |
258 | const struct devlink_trap_item *trap_item, |
259 | enum devlink_command cmd, u32 portid, u32 seq, |
260 | int flags) |
261 | { |
262 | struct devlink_trap_group_item *group_item = trap_item->group_item; |
263 | void *hdr; |
264 | int err; |
265 | |
266 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
267 | if (!hdr) |
268 | return -EMSGSIZE; |
269 | |
270 | if (devlink_nl_put_handle(msg, devlink)) |
271 | goto nla_put_failure; |
272 | |
273 | if (nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_TRAP_GROUP_NAME, |
274 | str: group_item->group->name)) |
275 | goto nla_put_failure; |
276 | |
277 | if (nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_TRAP_NAME, str: trap_item->trap->name)) |
278 | goto nla_put_failure; |
279 | |
280 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_TRAP_TYPE, value: trap_item->trap->type)) |
281 | goto nla_put_failure; |
282 | |
283 | if (trap_item->trap->generic && |
284 | nla_put_flag(skb: msg, attrtype: DEVLINK_ATTR_TRAP_GENERIC)) |
285 | goto nla_put_failure; |
286 | |
287 | if (nla_put_u8(skb: msg, attrtype: DEVLINK_ATTR_TRAP_ACTION, value: trap_item->action)) |
288 | goto nla_put_failure; |
289 | |
290 | err = devlink_trap_metadata_put(msg, trap: trap_item->trap); |
291 | if (err) |
292 | goto nla_put_failure; |
293 | |
294 | err = devlink_trap_stats_put(msg, devlink, trap_item); |
295 | if (err) |
296 | goto nla_put_failure; |
297 | |
298 | genlmsg_end(skb: msg, hdr); |
299 | |
300 | return 0; |
301 | |
302 | nla_put_failure: |
303 | genlmsg_cancel(skb: msg, hdr); |
304 | return -EMSGSIZE; |
305 | } |
306 | |
307 | int devlink_nl_trap_get_doit(struct sk_buff *skb, struct genl_info *info) |
308 | { |
309 | struct netlink_ext_ack *extack = info->extack; |
310 | struct devlink *devlink = info->user_ptr[0]; |
311 | struct devlink_trap_item *trap_item; |
312 | struct sk_buff *msg; |
313 | int err; |
314 | |
315 | if (list_empty(head: &devlink->trap_list)) |
316 | return -EOPNOTSUPP; |
317 | |
318 | trap_item = devlink_trap_item_get_from_info(devlink, info); |
319 | if (!trap_item) { |
320 | NL_SET_ERR_MSG(extack, "Device did not register this trap" ); |
321 | return -ENOENT; |
322 | } |
323 | |
324 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
325 | if (!msg) |
326 | return -ENOMEM; |
327 | |
328 | err = devlink_nl_trap_fill(msg, devlink, trap_item, |
329 | cmd: DEVLINK_CMD_TRAP_NEW, portid: info->snd_portid, |
330 | seq: info->snd_seq, flags: 0); |
331 | if (err) |
332 | goto err_trap_fill; |
333 | |
334 | return genlmsg_reply(skb: msg, info); |
335 | |
336 | err_trap_fill: |
337 | nlmsg_free(skb: msg); |
338 | return err; |
339 | } |
340 | |
341 | static int devlink_nl_trap_get_dump_one(struct sk_buff *msg, |
342 | struct devlink *devlink, |
343 | struct netlink_callback *cb, int flags) |
344 | { |
345 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
346 | struct devlink_trap_item *trap_item; |
347 | int idx = 0; |
348 | int err = 0; |
349 | |
350 | list_for_each_entry(trap_item, &devlink->trap_list, list) { |
351 | if (idx < state->idx) { |
352 | idx++; |
353 | continue; |
354 | } |
355 | err = devlink_nl_trap_fill(msg, devlink, trap_item, |
356 | cmd: DEVLINK_CMD_TRAP_NEW, |
357 | NETLINK_CB(cb->skb).portid, |
358 | seq: cb->nlh->nlmsg_seq, flags); |
359 | if (err) { |
360 | state->idx = idx; |
361 | break; |
362 | } |
363 | idx++; |
364 | } |
365 | |
366 | return err; |
367 | } |
368 | |
369 | int devlink_nl_trap_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) |
370 | { |
371 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_trap_get_dump_one); |
372 | } |
373 | |
374 | static int __devlink_trap_action_set(struct devlink *devlink, |
375 | struct devlink_trap_item *trap_item, |
376 | enum devlink_trap_action trap_action, |
377 | struct netlink_ext_ack *extack) |
378 | { |
379 | int err; |
380 | |
381 | if (trap_item->action != trap_action && |
382 | trap_item->trap->type != DEVLINK_TRAP_TYPE_DROP) { |
383 | NL_SET_ERR_MSG(extack, "Cannot change action of non-drop traps. Skipping" ); |
384 | return 0; |
385 | } |
386 | |
387 | err = devlink->ops->trap_action_set(devlink, trap_item->trap, |
388 | trap_action, extack); |
389 | if (err) |
390 | return err; |
391 | |
392 | trap_item->action = trap_action; |
393 | |
394 | return 0; |
395 | } |
396 | |
397 | static int devlink_trap_action_set(struct devlink *devlink, |
398 | struct devlink_trap_item *trap_item, |
399 | struct genl_info *info) |
400 | { |
401 | enum devlink_trap_action trap_action; |
402 | int err; |
403 | |
404 | if (!info->attrs[DEVLINK_ATTR_TRAP_ACTION]) |
405 | return 0; |
406 | |
407 | err = devlink_trap_action_get_from_info(info, p_trap_action: &trap_action); |
408 | if (err) { |
409 | NL_SET_ERR_MSG(info->extack, "Invalid trap action" ); |
410 | return -EINVAL; |
411 | } |
412 | |
413 | return __devlink_trap_action_set(devlink, trap_item, trap_action, |
414 | extack: info->extack); |
415 | } |
416 | |
417 | int devlink_nl_trap_set_doit(struct sk_buff *skb, struct genl_info *info) |
418 | { |
419 | struct netlink_ext_ack *extack = info->extack; |
420 | struct devlink *devlink = info->user_ptr[0]; |
421 | struct devlink_trap_item *trap_item; |
422 | |
423 | if (list_empty(head: &devlink->trap_list)) |
424 | return -EOPNOTSUPP; |
425 | |
426 | trap_item = devlink_trap_item_get_from_info(devlink, info); |
427 | if (!trap_item) { |
428 | NL_SET_ERR_MSG(extack, "Device did not register this trap" ); |
429 | return -ENOENT; |
430 | } |
431 | |
432 | return devlink_trap_action_set(devlink, trap_item, info); |
433 | } |
434 | |
435 | static struct devlink_trap_group_item * |
436 | devlink_trap_group_item_lookup(struct devlink *devlink, const char *name) |
437 | { |
438 | struct devlink_trap_group_item *group_item; |
439 | |
440 | list_for_each_entry(group_item, &devlink->trap_group_list, list) { |
441 | if (!strcmp(group_item->group->name, name)) |
442 | return group_item; |
443 | } |
444 | |
445 | return NULL; |
446 | } |
447 | |
448 | static struct devlink_trap_group_item * |
449 | devlink_trap_group_item_lookup_by_id(struct devlink *devlink, u16 id) |
450 | { |
451 | struct devlink_trap_group_item *group_item; |
452 | |
453 | list_for_each_entry(group_item, &devlink->trap_group_list, list) { |
454 | if (group_item->group->id == id) |
455 | return group_item; |
456 | } |
457 | |
458 | return NULL; |
459 | } |
460 | |
461 | static struct devlink_trap_group_item * |
462 | devlink_trap_group_item_get_from_info(struct devlink *devlink, |
463 | struct genl_info *info) |
464 | { |
465 | char *name; |
466 | |
467 | if (!info->attrs[DEVLINK_ATTR_TRAP_GROUP_NAME]) |
468 | return NULL; |
469 | name = nla_data(nla: info->attrs[DEVLINK_ATTR_TRAP_GROUP_NAME]); |
470 | |
471 | return devlink_trap_group_item_lookup(devlink, name); |
472 | } |
473 | |
474 | static int |
475 | devlink_nl_trap_group_fill(struct sk_buff *msg, struct devlink *devlink, |
476 | const struct devlink_trap_group_item *group_item, |
477 | enum devlink_command cmd, u32 portid, u32 seq, |
478 | int flags) |
479 | { |
480 | void *hdr; |
481 | int err; |
482 | |
483 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
484 | if (!hdr) |
485 | return -EMSGSIZE; |
486 | |
487 | if (devlink_nl_put_handle(msg, devlink)) |
488 | goto nla_put_failure; |
489 | |
490 | if (nla_put_string(skb: msg, attrtype: DEVLINK_ATTR_TRAP_GROUP_NAME, |
491 | str: group_item->group->name)) |
492 | goto nla_put_failure; |
493 | |
494 | if (group_item->group->generic && |
495 | nla_put_flag(skb: msg, attrtype: DEVLINK_ATTR_TRAP_GENERIC)) |
496 | goto nla_put_failure; |
497 | |
498 | if (group_item->policer_item && |
499 | nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_TRAP_POLICER_ID, |
500 | value: group_item->policer_item->policer->id)) |
501 | goto nla_put_failure; |
502 | |
503 | err = devlink_trap_group_stats_put(msg, trap_stats: group_item->stats); |
504 | if (err) |
505 | goto nla_put_failure; |
506 | |
507 | genlmsg_end(skb: msg, hdr); |
508 | |
509 | return 0; |
510 | |
511 | nla_put_failure: |
512 | genlmsg_cancel(skb: msg, hdr); |
513 | return -EMSGSIZE; |
514 | } |
515 | |
516 | int devlink_nl_trap_group_get_doit(struct sk_buff *skb, struct genl_info *info) |
517 | { |
518 | struct netlink_ext_ack *extack = info->extack; |
519 | struct devlink *devlink = info->user_ptr[0]; |
520 | struct devlink_trap_group_item *group_item; |
521 | struct sk_buff *msg; |
522 | int err; |
523 | |
524 | if (list_empty(head: &devlink->trap_group_list)) |
525 | return -EOPNOTSUPP; |
526 | |
527 | group_item = devlink_trap_group_item_get_from_info(devlink, info); |
528 | if (!group_item) { |
529 | NL_SET_ERR_MSG(extack, "Device did not register this trap group" ); |
530 | return -ENOENT; |
531 | } |
532 | |
533 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
534 | if (!msg) |
535 | return -ENOMEM; |
536 | |
537 | err = devlink_nl_trap_group_fill(msg, devlink, group_item, |
538 | cmd: DEVLINK_CMD_TRAP_GROUP_NEW, |
539 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
540 | if (err) |
541 | goto err_trap_group_fill; |
542 | |
543 | return genlmsg_reply(skb: msg, info); |
544 | |
545 | err_trap_group_fill: |
546 | nlmsg_free(skb: msg); |
547 | return err; |
548 | } |
549 | |
550 | static int devlink_nl_trap_group_get_dump_one(struct sk_buff *msg, |
551 | struct devlink *devlink, |
552 | struct netlink_callback *cb, |
553 | int flags) |
554 | { |
555 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
556 | struct devlink_trap_group_item *group_item; |
557 | int idx = 0; |
558 | int err = 0; |
559 | |
560 | list_for_each_entry(group_item, &devlink->trap_group_list, list) { |
561 | if (idx < state->idx) { |
562 | idx++; |
563 | continue; |
564 | } |
565 | err = devlink_nl_trap_group_fill(msg, devlink, group_item, |
566 | cmd: DEVLINK_CMD_TRAP_GROUP_NEW, |
567 | NETLINK_CB(cb->skb).portid, |
568 | seq: cb->nlh->nlmsg_seq, flags); |
569 | if (err) { |
570 | state->idx = idx; |
571 | break; |
572 | } |
573 | idx++; |
574 | } |
575 | |
576 | return err; |
577 | } |
578 | |
579 | int devlink_nl_trap_group_get_dumpit(struct sk_buff *skb, |
580 | struct netlink_callback *cb) |
581 | { |
582 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_trap_group_get_dump_one); |
583 | } |
584 | |
585 | static int |
586 | __devlink_trap_group_action_set(struct devlink *devlink, |
587 | struct devlink_trap_group_item *group_item, |
588 | enum devlink_trap_action trap_action, |
589 | struct netlink_ext_ack *extack) |
590 | { |
591 | const char *group_name = group_item->group->name; |
592 | struct devlink_trap_item *trap_item; |
593 | int err; |
594 | |
595 | if (devlink->ops->trap_group_action_set) { |
596 | err = devlink->ops->trap_group_action_set(devlink, group_item->group, |
597 | trap_action, extack); |
598 | if (err) |
599 | return err; |
600 | |
601 | list_for_each_entry(trap_item, &devlink->trap_list, list) { |
602 | if (strcmp(trap_item->group_item->group->name, group_name)) |
603 | continue; |
604 | if (trap_item->action != trap_action && |
605 | trap_item->trap->type != DEVLINK_TRAP_TYPE_DROP) |
606 | continue; |
607 | trap_item->action = trap_action; |
608 | } |
609 | |
610 | return 0; |
611 | } |
612 | |
613 | list_for_each_entry(trap_item, &devlink->trap_list, list) { |
614 | if (strcmp(trap_item->group_item->group->name, group_name)) |
615 | continue; |
616 | err = __devlink_trap_action_set(devlink, trap_item, |
617 | trap_action, extack); |
618 | if (err) |
619 | return err; |
620 | } |
621 | |
622 | return 0; |
623 | } |
624 | |
625 | static int |
626 | devlink_trap_group_action_set(struct devlink *devlink, |
627 | struct devlink_trap_group_item *group_item, |
628 | struct genl_info *info, bool *p_modified) |
629 | { |
630 | enum devlink_trap_action trap_action; |
631 | int err; |
632 | |
633 | if (!info->attrs[DEVLINK_ATTR_TRAP_ACTION]) |
634 | return 0; |
635 | |
636 | err = devlink_trap_action_get_from_info(info, p_trap_action: &trap_action); |
637 | if (err) { |
638 | NL_SET_ERR_MSG(info->extack, "Invalid trap action" ); |
639 | return -EINVAL; |
640 | } |
641 | |
642 | err = __devlink_trap_group_action_set(devlink, group_item, trap_action, |
643 | extack: info->extack); |
644 | if (err) |
645 | return err; |
646 | |
647 | *p_modified = true; |
648 | |
649 | return 0; |
650 | } |
651 | |
652 | static int devlink_trap_group_set(struct devlink *devlink, |
653 | struct devlink_trap_group_item *group_item, |
654 | struct genl_info *info) |
655 | { |
656 | struct devlink_trap_policer_item *policer_item; |
657 | struct netlink_ext_ack *extack = info->extack; |
658 | const struct devlink_trap_policer *policer; |
659 | struct nlattr **attrs = info->attrs; |
660 | u32 policer_id; |
661 | int err; |
662 | |
663 | if (!attrs[DEVLINK_ATTR_TRAP_POLICER_ID]) |
664 | return 0; |
665 | |
666 | if (!devlink->ops->trap_group_set) |
667 | return -EOPNOTSUPP; |
668 | |
669 | policer_id = nla_get_u32(nla: attrs[DEVLINK_ATTR_TRAP_POLICER_ID]); |
670 | policer_item = devlink_trap_policer_item_lookup(devlink, id: policer_id); |
671 | if (policer_id && !policer_item) { |
672 | NL_SET_ERR_MSG(extack, "Device did not register this trap policer" ); |
673 | return -ENOENT; |
674 | } |
675 | policer = policer_item ? policer_item->policer : NULL; |
676 | |
677 | err = devlink->ops->trap_group_set(devlink, group_item->group, policer, |
678 | extack); |
679 | if (err) |
680 | return err; |
681 | |
682 | group_item->policer_item = policer_item; |
683 | |
684 | return 0; |
685 | } |
686 | |
687 | int devlink_nl_trap_group_set_doit(struct sk_buff *skb, struct genl_info *info) |
688 | { |
689 | struct netlink_ext_ack *extack = info->extack; |
690 | struct devlink *devlink = info->user_ptr[0]; |
691 | struct devlink_trap_group_item *group_item; |
692 | bool modified = false; |
693 | int err; |
694 | |
695 | if (list_empty(head: &devlink->trap_group_list)) |
696 | return -EOPNOTSUPP; |
697 | |
698 | group_item = devlink_trap_group_item_get_from_info(devlink, info); |
699 | if (!group_item) { |
700 | NL_SET_ERR_MSG(extack, "Device did not register this trap group" ); |
701 | return -ENOENT; |
702 | } |
703 | |
704 | err = devlink_trap_group_action_set(devlink, group_item, info, |
705 | p_modified: &modified); |
706 | if (err) |
707 | return err; |
708 | |
709 | err = devlink_trap_group_set(devlink, group_item, info); |
710 | if (err) |
711 | goto err_trap_group_set; |
712 | |
713 | return 0; |
714 | |
715 | err_trap_group_set: |
716 | if (modified) |
717 | NL_SET_ERR_MSG(extack, "Trap group set failed, but some changes were committed already" ); |
718 | return err; |
719 | } |
720 | |
721 | static struct devlink_trap_policer_item * |
722 | devlink_trap_policer_item_get_from_info(struct devlink *devlink, |
723 | struct genl_info *info) |
724 | { |
725 | u32 id; |
726 | |
727 | if (!info->attrs[DEVLINK_ATTR_TRAP_POLICER_ID]) |
728 | return NULL; |
729 | id = nla_get_u32(nla: info->attrs[DEVLINK_ATTR_TRAP_POLICER_ID]); |
730 | |
731 | return devlink_trap_policer_item_lookup(devlink, id); |
732 | } |
733 | |
734 | static int |
735 | devlink_trap_policer_stats_put(struct sk_buff *msg, struct devlink *devlink, |
736 | const struct devlink_trap_policer *policer) |
737 | { |
738 | struct nlattr *attr; |
739 | u64 drops; |
740 | int err; |
741 | |
742 | if (!devlink->ops->trap_policer_counter_get) |
743 | return 0; |
744 | |
745 | err = devlink->ops->trap_policer_counter_get(devlink, policer, &drops); |
746 | if (err) |
747 | return err; |
748 | |
749 | attr = nla_nest_start(skb: msg, attrtype: DEVLINK_ATTR_STATS); |
750 | if (!attr) |
751 | return -EMSGSIZE; |
752 | |
753 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_STATS_RX_DROPPED, value: drops, |
754 | padattr: DEVLINK_ATTR_PAD)) |
755 | goto nla_put_failure; |
756 | |
757 | nla_nest_end(skb: msg, start: attr); |
758 | |
759 | return 0; |
760 | |
761 | nla_put_failure: |
762 | nla_nest_cancel(skb: msg, start: attr); |
763 | return -EMSGSIZE; |
764 | } |
765 | |
766 | static int |
767 | devlink_nl_trap_policer_fill(struct sk_buff *msg, struct devlink *devlink, |
768 | const struct devlink_trap_policer_item *policer_item, |
769 | enum devlink_command cmd, u32 portid, u32 seq, |
770 | int flags) |
771 | { |
772 | void *hdr; |
773 | int err; |
774 | |
775 | hdr = genlmsg_put(skb: msg, portid, seq, family: &devlink_nl_family, flags, cmd); |
776 | if (!hdr) |
777 | return -EMSGSIZE; |
778 | |
779 | if (devlink_nl_put_handle(msg, devlink)) |
780 | goto nla_put_failure; |
781 | |
782 | if (nla_put_u32(skb: msg, attrtype: DEVLINK_ATTR_TRAP_POLICER_ID, |
783 | value: policer_item->policer->id)) |
784 | goto nla_put_failure; |
785 | |
786 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_TRAP_POLICER_RATE, |
787 | value: policer_item->rate, padattr: DEVLINK_ATTR_PAD)) |
788 | goto nla_put_failure; |
789 | |
790 | if (nla_put_u64_64bit(skb: msg, attrtype: DEVLINK_ATTR_TRAP_POLICER_BURST, |
791 | value: policer_item->burst, padattr: DEVLINK_ATTR_PAD)) |
792 | goto nla_put_failure; |
793 | |
794 | err = devlink_trap_policer_stats_put(msg, devlink, |
795 | policer: policer_item->policer); |
796 | if (err) |
797 | goto nla_put_failure; |
798 | |
799 | genlmsg_end(skb: msg, hdr); |
800 | |
801 | return 0; |
802 | |
803 | nla_put_failure: |
804 | genlmsg_cancel(skb: msg, hdr); |
805 | return -EMSGSIZE; |
806 | } |
807 | |
808 | int devlink_nl_trap_policer_get_doit(struct sk_buff *skb, |
809 | struct genl_info *info) |
810 | { |
811 | struct devlink_trap_policer_item *policer_item; |
812 | struct netlink_ext_ack *extack = info->extack; |
813 | struct devlink *devlink = info->user_ptr[0]; |
814 | struct sk_buff *msg; |
815 | int err; |
816 | |
817 | if (list_empty(head: &devlink->trap_policer_list)) |
818 | return -EOPNOTSUPP; |
819 | |
820 | policer_item = devlink_trap_policer_item_get_from_info(devlink, info); |
821 | if (!policer_item) { |
822 | NL_SET_ERR_MSG(extack, "Device did not register this trap policer" ); |
823 | return -ENOENT; |
824 | } |
825 | |
826 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
827 | if (!msg) |
828 | return -ENOMEM; |
829 | |
830 | err = devlink_nl_trap_policer_fill(msg, devlink, policer_item, |
831 | cmd: DEVLINK_CMD_TRAP_POLICER_NEW, |
832 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
833 | if (err) |
834 | goto err_trap_policer_fill; |
835 | |
836 | return genlmsg_reply(skb: msg, info); |
837 | |
838 | err_trap_policer_fill: |
839 | nlmsg_free(skb: msg); |
840 | return err; |
841 | } |
842 | |
843 | static int devlink_nl_trap_policer_get_dump_one(struct sk_buff *msg, |
844 | struct devlink *devlink, |
845 | struct netlink_callback *cb, |
846 | int flags) |
847 | { |
848 | struct devlink_nl_dump_state *state = devlink_dump_state(cb); |
849 | struct devlink_trap_policer_item *policer_item; |
850 | int idx = 0; |
851 | int err = 0; |
852 | |
853 | list_for_each_entry(policer_item, &devlink->trap_policer_list, list) { |
854 | if (idx < state->idx) { |
855 | idx++; |
856 | continue; |
857 | } |
858 | err = devlink_nl_trap_policer_fill(msg, devlink, policer_item, |
859 | cmd: DEVLINK_CMD_TRAP_POLICER_NEW, |
860 | NETLINK_CB(cb->skb).portid, |
861 | seq: cb->nlh->nlmsg_seq, flags); |
862 | if (err) { |
863 | state->idx = idx; |
864 | break; |
865 | } |
866 | idx++; |
867 | } |
868 | |
869 | return err; |
870 | } |
871 | |
872 | int devlink_nl_trap_policer_get_dumpit(struct sk_buff *skb, |
873 | struct netlink_callback *cb) |
874 | { |
875 | return devlink_nl_dumpit(msg: skb, cb, dump_one: devlink_nl_trap_policer_get_dump_one); |
876 | } |
877 | |
878 | static int |
879 | devlink_trap_policer_set(struct devlink *devlink, |
880 | struct devlink_trap_policer_item *policer_item, |
881 | struct genl_info *info) |
882 | { |
883 | struct netlink_ext_ack *extack = info->extack; |
884 | struct nlattr **attrs = info->attrs; |
885 | u64 rate, burst; |
886 | int err; |
887 | |
888 | rate = policer_item->rate; |
889 | burst = policer_item->burst; |
890 | |
891 | if (attrs[DEVLINK_ATTR_TRAP_POLICER_RATE]) |
892 | rate = nla_get_u64(nla: attrs[DEVLINK_ATTR_TRAP_POLICER_RATE]); |
893 | |
894 | if (attrs[DEVLINK_ATTR_TRAP_POLICER_BURST]) |
895 | burst = nla_get_u64(nla: attrs[DEVLINK_ATTR_TRAP_POLICER_BURST]); |
896 | |
897 | if (rate < policer_item->policer->min_rate) { |
898 | NL_SET_ERR_MSG(extack, "Policer rate lower than limit" ); |
899 | return -EINVAL; |
900 | } |
901 | |
902 | if (rate > policer_item->policer->max_rate) { |
903 | NL_SET_ERR_MSG(extack, "Policer rate higher than limit" ); |
904 | return -EINVAL; |
905 | } |
906 | |
907 | if (burst < policer_item->policer->min_burst) { |
908 | NL_SET_ERR_MSG(extack, "Policer burst size lower than limit" ); |
909 | return -EINVAL; |
910 | } |
911 | |
912 | if (burst > policer_item->policer->max_burst) { |
913 | NL_SET_ERR_MSG(extack, "Policer burst size higher than limit" ); |
914 | return -EINVAL; |
915 | } |
916 | |
917 | err = devlink->ops->trap_policer_set(devlink, policer_item->policer, |
918 | rate, burst, info->extack); |
919 | if (err) |
920 | return err; |
921 | |
922 | policer_item->rate = rate; |
923 | policer_item->burst = burst; |
924 | |
925 | return 0; |
926 | } |
927 | |
928 | int devlink_nl_trap_policer_set_doit(struct sk_buff *skb, |
929 | struct genl_info *info) |
930 | { |
931 | struct devlink_trap_policer_item *policer_item; |
932 | struct netlink_ext_ack *extack = info->extack; |
933 | struct devlink *devlink = info->user_ptr[0]; |
934 | |
935 | if (list_empty(head: &devlink->trap_policer_list)) |
936 | return -EOPNOTSUPP; |
937 | |
938 | if (!devlink->ops->trap_policer_set) |
939 | return -EOPNOTSUPP; |
940 | |
941 | policer_item = devlink_trap_policer_item_get_from_info(devlink, info); |
942 | if (!policer_item) { |
943 | NL_SET_ERR_MSG(extack, "Device did not register this trap policer" ); |
944 | return -ENOENT; |
945 | } |
946 | |
947 | return devlink_trap_policer_set(devlink, policer_item, info); |
948 | } |
949 | |
950 | #define DEVLINK_TRAP(_id, _type) \ |
951 | { \ |
952 | .type = DEVLINK_TRAP_TYPE_##_type, \ |
953 | .id = DEVLINK_TRAP_GENERIC_ID_##_id, \ |
954 | .name = DEVLINK_TRAP_GENERIC_NAME_##_id, \ |
955 | } |
956 | |
957 | static const struct devlink_trap devlink_trap_generic[] = { |
958 | DEVLINK_TRAP(SMAC_MC, DROP), |
959 | DEVLINK_TRAP(VLAN_TAG_MISMATCH, DROP), |
960 | DEVLINK_TRAP(INGRESS_VLAN_FILTER, DROP), |
961 | DEVLINK_TRAP(INGRESS_STP_FILTER, DROP), |
962 | DEVLINK_TRAP(EMPTY_TX_LIST, DROP), |
963 | DEVLINK_TRAP(PORT_LOOPBACK_FILTER, DROP), |
964 | DEVLINK_TRAP(BLACKHOLE_ROUTE, DROP), |
965 | DEVLINK_TRAP(TTL_ERROR, EXCEPTION), |
966 | DEVLINK_TRAP(TAIL_DROP, DROP), |
967 | DEVLINK_TRAP(NON_IP_PACKET, DROP), |
968 | DEVLINK_TRAP(UC_DIP_MC_DMAC, DROP), |
969 | DEVLINK_TRAP(DIP_LB, DROP), |
970 | DEVLINK_TRAP(SIP_MC, DROP), |
971 | DEVLINK_TRAP(SIP_LB, DROP), |
972 | DEVLINK_TRAP(CORRUPTED_IP_HDR, DROP), |
973 | DEVLINK_TRAP(IPV4_SIP_BC, DROP), |
974 | DEVLINK_TRAP(IPV6_MC_DIP_RESERVED_SCOPE, DROP), |
975 | DEVLINK_TRAP(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, DROP), |
976 | DEVLINK_TRAP(MTU_ERROR, EXCEPTION), |
977 | DEVLINK_TRAP(UNRESOLVED_NEIGH, EXCEPTION), |
978 | DEVLINK_TRAP(RPF, EXCEPTION), |
979 | DEVLINK_TRAP(REJECT_ROUTE, EXCEPTION), |
980 | DEVLINK_TRAP(IPV4_LPM_UNICAST_MISS, EXCEPTION), |
981 | DEVLINK_TRAP(IPV6_LPM_UNICAST_MISS, EXCEPTION), |
982 | DEVLINK_TRAP(NON_ROUTABLE, DROP), |
983 | DEVLINK_TRAP(DECAP_ERROR, EXCEPTION), |
984 | DEVLINK_TRAP(OVERLAY_SMAC_MC, DROP), |
985 | DEVLINK_TRAP(INGRESS_FLOW_ACTION_DROP, DROP), |
986 | DEVLINK_TRAP(EGRESS_FLOW_ACTION_DROP, DROP), |
987 | DEVLINK_TRAP(STP, CONTROL), |
988 | DEVLINK_TRAP(LACP, CONTROL), |
989 | DEVLINK_TRAP(LLDP, CONTROL), |
990 | DEVLINK_TRAP(IGMP_QUERY, CONTROL), |
991 | DEVLINK_TRAP(IGMP_V1_REPORT, CONTROL), |
992 | DEVLINK_TRAP(IGMP_V2_REPORT, CONTROL), |
993 | DEVLINK_TRAP(IGMP_V3_REPORT, CONTROL), |
994 | DEVLINK_TRAP(IGMP_V2_LEAVE, CONTROL), |
995 | DEVLINK_TRAP(MLD_QUERY, CONTROL), |
996 | DEVLINK_TRAP(MLD_V1_REPORT, CONTROL), |
997 | DEVLINK_TRAP(MLD_V2_REPORT, CONTROL), |
998 | DEVLINK_TRAP(MLD_V1_DONE, CONTROL), |
999 | DEVLINK_TRAP(IPV4_DHCP, CONTROL), |
1000 | DEVLINK_TRAP(IPV6_DHCP, CONTROL), |
1001 | DEVLINK_TRAP(ARP_REQUEST, CONTROL), |
1002 | DEVLINK_TRAP(ARP_RESPONSE, CONTROL), |
1003 | DEVLINK_TRAP(ARP_OVERLAY, CONTROL), |
1004 | DEVLINK_TRAP(IPV6_NEIGH_SOLICIT, CONTROL), |
1005 | DEVLINK_TRAP(IPV6_NEIGH_ADVERT, CONTROL), |
1006 | DEVLINK_TRAP(IPV4_BFD, CONTROL), |
1007 | DEVLINK_TRAP(IPV6_BFD, CONTROL), |
1008 | DEVLINK_TRAP(IPV4_OSPF, CONTROL), |
1009 | DEVLINK_TRAP(IPV6_OSPF, CONTROL), |
1010 | DEVLINK_TRAP(IPV4_BGP, CONTROL), |
1011 | DEVLINK_TRAP(IPV6_BGP, CONTROL), |
1012 | DEVLINK_TRAP(IPV4_VRRP, CONTROL), |
1013 | DEVLINK_TRAP(IPV6_VRRP, CONTROL), |
1014 | DEVLINK_TRAP(IPV4_PIM, CONTROL), |
1015 | DEVLINK_TRAP(IPV6_PIM, CONTROL), |
1016 | DEVLINK_TRAP(UC_LB, CONTROL), |
1017 | DEVLINK_TRAP(LOCAL_ROUTE, CONTROL), |
1018 | DEVLINK_TRAP(EXTERNAL_ROUTE, CONTROL), |
1019 | DEVLINK_TRAP(IPV6_UC_DIP_LINK_LOCAL_SCOPE, CONTROL), |
1020 | DEVLINK_TRAP(IPV6_DIP_ALL_NODES, CONTROL), |
1021 | DEVLINK_TRAP(IPV6_DIP_ALL_ROUTERS, CONTROL), |
1022 | DEVLINK_TRAP(IPV6_ROUTER_SOLICIT, CONTROL), |
1023 | DEVLINK_TRAP(IPV6_ROUTER_ADVERT, CONTROL), |
1024 | DEVLINK_TRAP(IPV6_REDIRECT, CONTROL), |
1025 | DEVLINK_TRAP(IPV4_ROUTER_ALERT, CONTROL), |
1026 | DEVLINK_TRAP(IPV6_ROUTER_ALERT, CONTROL), |
1027 | DEVLINK_TRAP(PTP_EVENT, CONTROL), |
1028 | DEVLINK_TRAP(PTP_GENERAL, CONTROL), |
1029 | DEVLINK_TRAP(FLOW_ACTION_SAMPLE, CONTROL), |
1030 | DEVLINK_TRAP(FLOW_ACTION_TRAP, CONTROL), |
1031 | DEVLINK_TRAP(EARLY_DROP, DROP), |
1032 | DEVLINK_TRAP(VXLAN_PARSING, DROP), |
1033 | DEVLINK_TRAP(LLC_SNAP_PARSING, DROP), |
1034 | DEVLINK_TRAP(VLAN_PARSING, DROP), |
1035 | DEVLINK_TRAP(PPPOE_PPP_PARSING, DROP), |
1036 | DEVLINK_TRAP(MPLS_PARSING, DROP), |
1037 | DEVLINK_TRAP(ARP_PARSING, DROP), |
1038 | DEVLINK_TRAP(IP_1_PARSING, DROP), |
1039 | DEVLINK_TRAP(IP_N_PARSING, DROP), |
1040 | DEVLINK_TRAP(GRE_PARSING, DROP), |
1041 | DEVLINK_TRAP(UDP_PARSING, DROP), |
1042 | DEVLINK_TRAP(TCP_PARSING, DROP), |
1043 | DEVLINK_TRAP(IPSEC_PARSING, DROP), |
1044 | DEVLINK_TRAP(SCTP_PARSING, DROP), |
1045 | DEVLINK_TRAP(DCCP_PARSING, DROP), |
1046 | DEVLINK_TRAP(GTP_PARSING, DROP), |
1047 | DEVLINK_TRAP(ESP_PARSING, DROP), |
1048 | DEVLINK_TRAP(BLACKHOLE_NEXTHOP, DROP), |
1049 | DEVLINK_TRAP(DMAC_FILTER, DROP), |
1050 | DEVLINK_TRAP(EAPOL, CONTROL), |
1051 | DEVLINK_TRAP(LOCKED_PORT, DROP), |
1052 | }; |
1053 | |
1054 | #define DEVLINK_TRAP_GROUP(_id) \ |
1055 | { \ |
1056 | .id = DEVLINK_TRAP_GROUP_GENERIC_ID_##_id, \ |
1057 | .name = DEVLINK_TRAP_GROUP_GENERIC_NAME_##_id, \ |
1058 | } |
1059 | |
1060 | static const struct devlink_trap_group devlink_trap_group_generic[] = { |
1061 | DEVLINK_TRAP_GROUP(L2_DROPS), |
1062 | DEVLINK_TRAP_GROUP(L3_DROPS), |
1063 | DEVLINK_TRAP_GROUP(L3_EXCEPTIONS), |
1064 | DEVLINK_TRAP_GROUP(BUFFER_DROPS), |
1065 | DEVLINK_TRAP_GROUP(TUNNEL_DROPS), |
1066 | DEVLINK_TRAP_GROUP(ACL_DROPS), |
1067 | DEVLINK_TRAP_GROUP(STP), |
1068 | DEVLINK_TRAP_GROUP(LACP), |
1069 | DEVLINK_TRAP_GROUP(LLDP), |
1070 | DEVLINK_TRAP_GROUP(MC_SNOOPING), |
1071 | DEVLINK_TRAP_GROUP(DHCP), |
1072 | DEVLINK_TRAP_GROUP(NEIGH_DISCOVERY), |
1073 | DEVLINK_TRAP_GROUP(BFD), |
1074 | DEVLINK_TRAP_GROUP(OSPF), |
1075 | DEVLINK_TRAP_GROUP(BGP), |
1076 | DEVLINK_TRAP_GROUP(VRRP), |
1077 | DEVLINK_TRAP_GROUP(PIM), |
1078 | DEVLINK_TRAP_GROUP(UC_LB), |
1079 | DEVLINK_TRAP_GROUP(LOCAL_DELIVERY), |
1080 | DEVLINK_TRAP_GROUP(EXTERNAL_DELIVERY), |
1081 | DEVLINK_TRAP_GROUP(IPV6), |
1082 | DEVLINK_TRAP_GROUP(PTP_EVENT), |
1083 | DEVLINK_TRAP_GROUP(PTP_GENERAL), |
1084 | DEVLINK_TRAP_GROUP(ACL_SAMPLE), |
1085 | DEVLINK_TRAP_GROUP(ACL_TRAP), |
1086 | DEVLINK_TRAP_GROUP(PARSER_ERROR_DROPS), |
1087 | DEVLINK_TRAP_GROUP(EAPOL), |
1088 | }; |
1089 | |
1090 | static int devlink_trap_generic_verify(const struct devlink_trap *trap) |
1091 | { |
1092 | if (trap->id > DEVLINK_TRAP_GENERIC_ID_MAX) |
1093 | return -EINVAL; |
1094 | |
1095 | if (strcmp(trap->name, devlink_trap_generic[trap->id].name)) |
1096 | return -EINVAL; |
1097 | |
1098 | if (trap->type != devlink_trap_generic[trap->id].type) |
1099 | return -EINVAL; |
1100 | |
1101 | return 0; |
1102 | } |
1103 | |
1104 | static int devlink_trap_driver_verify(const struct devlink_trap *trap) |
1105 | { |
1106 | int i; |
1107 | |
1108 | if (trap->id <= DEVLINK_TRAP_GENERIC_ID_MAX) |
1109 | return -EINVAL; |
1110 | |
1111 | for (i = 0; i < ARRAY_SIZE(devlink_trap_generic); i++) { |
1112 | if (!strcmp(trap->name, devlink_trap_generic[i].name)) |
1113 | return -EEXIST; |
1114 | } |
1115 | |
1116 | return 0; |
1117 | } |
1118 | |
1119 | static int devlink_trap_verify(const struct devlink_trap *trap) |
1120 | { |
1121 | if (!trap || !trap->name) |
1122 | return -EINVAL; |
1123 | |
1124 | if (trap->generic) |
1125 | return devlink_trap_generic_verify(trap); |
1126 | else |
1127 | return devlink_trap_driver_verify(trap); |
1128 | } |
1129 | |
1130 | static int |
1131 | devlink_trap_group_generic_verify(const struct devlink_trap_group *group) |
1132 | { |
1133 | if (group->id > DEVLINK_TRAP_GROUP_GENERIC_ID_MAX) |
1134 | return -EINVAL; |
1135 | |
1136 | if (strcmp(group->name, devlink_trap_group_generic[group->id].name)) |
1137 | return -EINVAL; |
1138 | |
1139 | return 0; |
1140 | } |
1141 | |
1142 | static int |
1143 | devlink_trap_group_driver_verify(const struct devlink_trap_group *group) |
1144 | { |
1145 | int i; |
1146 | |
1147 | if (group->id <= DEVLINK_TRAP_GROUP_GENERIC_ID_MAX) |
1148 | return -EINVAL; |
1149 | |
1150 | for (i = 0; i < ARRAY_SIZE(devlink_trap_group_generic); i++) { |
1151 | if (!strcmp(group->name, devlink_trap_group_generic[i].name)) |
1152 | return -EEXIST; |
1153 | } |
1154 | |
1155 | return 0; |
1156 | } |
1157 | |
1158 | static int devlink_trap_group_verify(const struct devlink_trap_group *group) |
1159 | { |
1160 | if (group->generic) |
1161 | return devlink_trap_group_generic_verify(group); |
1162 | else |
1163 | return devlink_trap_group_driver_verify(group); |
1164 | } |
1165 | |
1166 | static void |
1167 | devlink_trap_group_notify(struct devlink *devlink, |
1168 | const struct devlink_trap_group_item *group_item, |
1169 | enum devlink_command cmd) |
1170 | { |
1171 | struct sk_buff *msg; |
1172 | int err; |
1173 | |
1174 | WARN_ON_ONCE(cmd != DEVLINK_CMD_TRAP_GROUP_NEW && |
1175 | cmd != DEVLINK_CMD_TRAP_GROUP_DEL); |
1176 | if (!xa_get_mark(&devlinks, index: devlink->index, DEVLINK_REGISTERED)) |
1177 | return; |
1178 | |
1179 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1180 | if (!msg) |
1181 | return; |
1182 | |
1183 | err = devlink_nl_trap_group_fill(msg, devlink, group_item, cmd, portid: 0, seq: 0, |
1184 | flags: 0); |
1185 | if (err) { |
1186 | nlmsg_free(skb: msg); |
1187 | return; |
1188 | } |
1189 | |
1190 | genlmsg_multicast_netns(family: &devlink_nl_family, net: devlink_net(devlink), |
1191 | skb: msg, portid: 0, group: DEVLINK_MCGRP_CONFIG, GFP_KERNEL); |
1192 | } |
1193 | |
1194 | void devlink_trap_groups_notify_register(struct devlink *devlink) |
1195 | { |
1196 | struct devlink_trap_group_item *group_item; |
1197 | |
1198 | list_for_each_entry(group_item, &devlink->trap_group_list, list) |
1199 | devlink_trap_group_notify(devlink, group_item, |
1200 | cmd: DEVLINK_CMD_TRAP_GROUP_NEW); |
1201 | } |
1202 | |
1203 | void devlink_trap_groups_notify_unregister(struct devlink *devlink) |
1204 | { |
1205 | struct devlink_trap_group_item *group_item; |
1206 | |
1207 | list_for_each_entry_reverse(group_item, &devlink->trap_group_list, list) |
1208 | devlink_trap_group_notify(devlink, group_item, |
1209 | cmd: DEVLINK_CMD_TRAP_GROUP_DEL); |
1210 | } |
1211 | |
1212 | static int |
1213 | devlink_trap_item_group_link(struct devlink *devlink, |
1214 | struct devlink_trap_item *trap_item) |
1215 | { |
1216 | u16 group_id = trap_item->trap->init_group_id; |
1217 | struct devlink_trap_group_item *group_item; |
1218 | |
1219 | group_item = devlink_trap_group_item_lookup_by_id(devlink, id: group_id); |
1220 | if (WARN_ON_ONCE(!group_item)) |
1221 | return -EINVAL; |
1222 | |
1223 | trap_item->group_item = group_item; |
1224 | |
1225 | return 0; |
1226 | } |
1227 | |
1228 | static void devlink_trap_notify(struct devlink *devlink, |
1229 | const struct devlink_trap_item *trap_item, |
1230 | enum devlink_command cmd) |
1231 | { |
1232 | struct sk_buff *msg; |
1233 | int err; |
1234 | |
1235 | WARN_ON_ONCE(cmd != DEVLINK_CMD_TRAP_NEW && |
1236 | cmd != DEVLINK_CMD_TRAP_DEL); |
1237 | if (!xa_get_mark(&devlinks, index: devlink->index, DEVLINK_REGISTERED)) |
1238 | return; |
1239 | |
1240 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1241 | if (!msg) |
1242 | return; |
1243 | |
1244 | err = devlink_nl_trap_fill(msg, devlink, trap_item, cmd, portid: 0, seq: 0, flags: 0); |
1245 | if (err) { |
1246 | nlmsg_free(skb: msg); |
1247 | return; |
1248 | } |
1249 | |
1250 | genlmsg_multicast_netns(family: &devlink_nl_family, net: devlink_net(devlink), |
1251 | skb: msg, portid: 0, group: DEVLINK_MCGRP_CONFIG, GFP_KERNEL); |
1252 | } |
1253 | |
1254 | void devlink_traps_notify_register(struct devlink *devlink) |
1255 | { |
1256 | struct devlink_trap_item *trap_item; |
1257 | |
1258 | list_for_each_entry(trap_item, &devlink->trap_list, list) |
1259 | devlink_trap_notify(devlink, trap_item, cmd: DEVLINK_CMD_TRAP_NEW); |
1260 | } |
1261 | |
1262 | void devlink_traps_notify_unregister(struct devlink *devlink) |
1263 | { |
1264 | struct devlink_trap_item *trap_item; |
1265 | |
1266 | list_for_each_entry_reverse(trap_item, &devlink->trap_list, list) |
1267 | devlink_trap_notify(devlink, trap_item, cmd: DEVLINK_CMD_TRAP_DEL); |
1268 | } |
1269 | |
1270 | static int |
1271 | devlink_trap_register(struct devlink *devlink, |
1272 | const struct devlink_trap *trap, void *priv) |
1273 | { |
1274 | struct devlink_trap_item *trap_item; |
1275 | int err; |
1276 | |
1277 | if (devlink_trap_item_lookup(devlink, name: trap->name)) |
1278 | return -EEXIST; |
1279 | |
1280 | trap_item = kzalloc(size: sizeof(*trap_item), GFP_KERNEL); |
1281 | if (!trap_item) |
1282 | return -ENOMEM; |
1283 | |
1284 | trap_item->stats = netdev_alloc_pcpu_stats(struct devlink_stats); |
1285 | if (!trap_item->stats) { |
1286 | err = -ENOMEM; |
1287 | goto err_stats_alloc; |
1288 | } |
1289 | |
1290 | trap_item->trap = trap; |
1291 | trap_item->action = trap->init_action; |
1292 | trap_item->priv = priv; |
1293 | |
1294 | err = devlink_trap_item_group_link(devlink, trap_item); |
1295 | if (err) |
1296 | goto err_group_link; |
1297 | |
1298 | err = devlink->ops->trap_init(devlink, trap, trap_item); |
1299 | if (err) |
1300 | goto err_trap_init; |
1301 | |
1302 | list_add_tail(new: &trap_item->list, head: &devlink->trap_list); |
1303 | devlink_trap_notify(devlink, trap_item, cmd: DEVLINK_CMD_TRAP_NEW); |
1304 | |
1305 | return 0; |
1306 | |
1307 | err_trap_init: |
1308 | err_group_link: |
1309 | free_percpu(pdata: trap_item->stats); |
1310 | err_stats_alloc: |
1311 | kfree(objp: trap_item); |
1312 | return err; |
1313 | } |
1314 | |
1315 | static void devlink_trap_unregister(struct devlink *devlink, |
1316 | const struct devlink_trap *trap) |
1317 | { |
1318 | struct devlink_trap_item *trap_item; |
1319 | |
1320 | trap_item = devlink_trap_item_lookup(devlink, name: trap->name); |
1321 | if (WARN_ON_ONCE(!trap_item)) |
1322 | return; |
1323 | |
1324 | devlink_trap_notify(devlink, trap_item, cmd: DEVLINK_CMD_TRAP_DEL); |
1325 | list_del(entry: &trap_item->list); |
1326 | if (devlink->ops->trap_fini) |
1327 | devlink->ops->trap_fini(devlink, trap, trap_item); |
1328 | free_percpu(pdata: trap_item->stats); |
1329 | kfree(objp: trap_item); |
1330 | } |
1331 | |
1332 | static void devlink_trap_disable(struct devlink *devlink, |
1333 | const struct devlink_trap *trap) |
1334 | { |
1335 | struct devlink_trap_item *trap_item; |
1336 | |
1337 | trap_item = devlink_trap_item_lookup(devlink, name: trap->name); |
1338 | if (WARN_ON_ONCE(!trap_item)) |
1339 | return; |
1340 | |
1341 | devlink->ops->trap_action_set(devlink, trap, DEVLINK_TRAP_ACTION_DROP, |
1342 | NULL); |
1343 | trap_item->action = DEVLINK_TRAP_ACTION_DROP; |
1344 | } |
1345 | |
1346 | /** |
1347 | * devl_traps_register - Register packet traps with devlink. |
1348 | * @devlink: devlink. |
1349 | * @traps: Packet traps. |
1350 | * @traps_count: Count of provided packet traps. |
1351 | * @priv: Driver private information. |
1352 | * |
1353 | * Return: Non-zero value on failure. |
1354 | */ |
1355 | int devl_traps_register(struct devlink *devlink, |
1356 | const struct devlink_trap *traps, |
1357 | size_t traps_count, void *priv) |
1358 | { |
1359 | int i, err; |
1360 | |
1361 | if (!devlink->ops->trap_init || !devlink->ops->trap_action_set) |
1362 | return -EINVAL; |
1363 | |
1364 | devl_assert_locked(devlink); |
1365 | for (i = 0; i < traps_count; i++) { |
1366 | const struct devlink_trap *trap = &traps[i]; |
1367 | |
1368 | err = devlink_trap_verify(trap); |
1369 | if (err) |
1370 | goto err_trap_verify; |
1371 | |
1372 | err = devlink_trap_register(devlink, trap, priv); |
1373 | if (err) |
1374 | goto err_trap_register; |
1375 | } |
1376 | |
1377 | return 0; |
1378 | |
1379 | err_trap_register: |
1380 | err_trap_verify: |
1381 | for (i--; i >= 0; i--) |
1382 | devlink_trap_unregister(devlink, trap: &traps[i]); |
1383 | return err; |
1384 | } |
1385 | EXPORT_SYMBOL_GPL(devl_traps_register); |
1386 | |
1387 | /** |
1388 | * devlink_traps_register - Register packet traps with devlink. |
1389 | * @devlink: devlink. |
1390 | * @traps: Packet traps. |
1391 | * @traps_count: Count of provided packet traps. |
1392 | * @priv: Driver private information. |
1393 | * |
1394 | * Context: Takes and release devlink->lock <mutex>. |
1395 | * |
1396 | * Return: Non-zero value on failure. |
1397 | */ |
1398 | int devlink_traps_register(struct devlink *devlink, |
1399 | const struct devlink_trap *traps, |
1400 | size_t traps_count, void *priv) |
1401 | { |
1402 | int err; |
1403 | |
1404 | devl_lock(devlink); |
1405 | err = devl_traps_register(devlink, traps, traps_count, priv); |
1406 | devl_unlock(devlink); |
1407 | return err; |
1408 | } |
1409 | EXPORT_SYMBOL_GPL(devlink_traps_register); |
1410 | |
1411 | /** |
1412 | * devl_traps_unregister - Unregister packet traps from devlink. |
1413 | * @devlink: devlink. |
1414 | * @traps: Packet traps. |
1415 | * @traps_count: Count of provided packet traps. |
1416 | */ |
1417 | void devl_traps_unregister(struct devlink *devlink, |
1418 | const struct devlink_trap *traps, |
1419 | size_t traps_count) |
1420 | { |
1421 | int i; |
1422 | |
1423 | devl_assert_locked(devlink); |
1424 | /* Make sure we do not have any packets in-flight while unregistering |
1425 | * traps by disabling all of them and waiting for a grace period. |
1426 | */ |
1427 | for (i = traps_count - 1; i >= 0; i--) |
1428 | devlink_trap_disable(devlink, trap: &traps[i]); |
1429 | synchronize_rcu(); |
1430 | for (i = traps_count - 1; i >= 0; i--) |
1431 | devlink_trap_unregister(devlink, trap: &traps[i]); |
1432 | } |
1433 | EXPORT_SYMBOL_GPL(devl_traps_unregister); |
1434 | |
1435 | /** |
1436 | * devlink_traps_unregister - Unregister packet traps from devlink. |
1437 | * @devlink: devlink. |
1438 | * @traps: Packet traps. |
1439 | * @traps_count: Count of provided packet traps. |
1440 | * |
1441 | * Context: Takes and release devlink->lock <mutex>. |
1442 | */ |
1443 | void devlink_traps_unregister(struct devlink *devlink, |
1444 | const struct devlink_trap *traps, |
1445 | size_t traps_count) |
1446 | { |
1447 | devl_lock(devlink); |
1448 | devl_traps_unregister(devlink, traps, traps_count); |
1449 | devl_unlock(devlink); |
1450 | } |
1451 | EXPORT_SYMBOL_GPL(devlink_traps_unregister); |
1452 | |
1453 | static void |
1454 | devlink_trap_stats_update(struct devlink_stats __percpu *trap_stats, |
1455 | size_t skb_len) |
1456 | { |
1457 | struct devlink_stats *stats; |
1458 | |
1459 | stats = this_cpu_ptr(trap_stats); |
1460 | u64_stats_update_begin(syncp: &stats->syncp); |
1461 | u64_stats_add(p: &stats->rx_bytes, val: skb_len); |
1462 | u64_stats_inc(p: &stats->rx_packets); |
1463 | u64_stats_update_end(syncp: &stats->syncp); |
1464 | } |
1465 | |
1466 | static void |
1467 | devlink_trap_report_metadata_set(struct devlink_trap_metadata *metadata, |
1468 | const struct devlink_trap_item *trap_item, |
1469 | struct devlink_port *in_devlink_port, |
1470 | const struct flow_action_cookie *fa_cookie) |
1471 | { |
1472 | metadata->trap_name = trap_item->trap->name; |
1473 | metadata->trap_group_name = trap_item->group_item->group->name; |
1474 | metadata->fa_cookie = fa_cookie; |
1475 | metadata->trap_type = trap_item->trap->type; |
1476 | |
1477 | spin_lock(lock: &in_devlink_port->type_lock); |
1478 | if (in_devlink_port->type == DEVLINK_PORT_TYPE_ETH) |
1479 | metadata->input_dev = in_devlink_port->type_eth.netdev; |
1480 | spin_unlock(lock: &in_devlink_port->type_lock); |
1481 | } |
1482 | |
1483 | /** |
1484 | * devlink_trap_report - Report trapped packet to drop monitor. |
1485 | * @devlink: devlink. |
1486 | * @skb: Trapped packet. |
1487 | * @trap_ctx: Trap context. |
1488 | * @in_devlink_port: Input devlink port. |
1489 | * @fa_cookie: Flow action cookie. Could be NULL. |
1490 | */ |
1491 | void devlink_trap_report(struct devlink *devlink, struct sk_buff *skb, |
1492 | void *trap_ctx, struct devlink_port *in_devlink_port, |
1493 | const struct flow_action_cookie *fa_cookie) |
1494 | |
1495 | { |
1496 | struct devlink_trap_item *trap_item = trap_ctx; |
1497 | |
1498 | devlink_trap_stats_update(trap_stats: trap_item->stats, skb_len: skb->len); |
1499 | devlink_trap_stats_update(trap_stats: trap_item->group_item->stats, skb_len: skb->len); |
1500 | |
1501 | if (tracepoint_enabled(devlink_trap_report)) { |
1502 | struct devlink_trap_metadata metadata = {}; |
1503 | |
1504 | devlink_trap_report_metadata_set(metadata: &metadata, trap_item, |
1505 | in_devlink_port, fa_cookie); |
1506 | trace_devlink_trap_report(devlink, skb, metadata: &metadata); |
1507 | } |
1508 | } |
1509 | EXPORT_SYMBOL_GPL(devlink_trap_report); |
1510 | |
1511 | /** |
1512 | * devlink_trap_ctx_priv - Trap context to driver private information. |
1513 | * @trap_ctx: Trap context. |
1514 | * |
1515 | * Return: Driver private information passed during registration. |
1516 | */ |
1517 | void *devlink_trap_ctx_priv(void *trap_ctx) |
1518 | { |
1519 | struct devlink_trap_item *trap_item = trap_ctx; |
1520 | |
1521 | return trap_item->priv; |
1522 | } |
1523 | EXPORT_SYMBOL_GPL(devlink_trap_ctx_priv); |
1524 | |
1525 | static int |
1526 | devlink_trap_group_item_policer_link(struct devlink *devlink, |
1527 | struct devlink_trap_group_item *group_item) |
1528 | { |
1529 | u32 policer_id = group_item->group->init_policer_id; |
1530 | struct devlink_trap_policer_item *policer_item; |
1531 | |
1532 | if (policer_id == 0) |
1533 | return 0; |
1534 | |
1535 | policer_item = devlink_trap_policer_item_lookup(devlink, id: policer_id); |
1536 | if (WARN_ON_ONCE(!policer_item)) |
1537 | return -EINVAL; |
1538 | |
1539 | group_item->policer_item = policer_item; |
1540 | |
1541 | return 0; |
1542 | } |
1543 | |
1544 | static int |
1545 | devlink_trap_group_register(struct devlink *devlink, |
1546 | const struct devlink_trap_group *group) |
1547 | { |
1548 | struct devlink_trap_group_item *group_item; |
1549 | int err; |
1550 | |
1551 | if (devlink_trap_group_item_lookup(devlink, name: group->name)) |
1552 | return -EEXIST; |
1553 | |
1554 | group_item = kzalloc(size: sizeof(*group_item), GFP_KERNEL); |
1555 | if (!group_item) |
1556 | return -ENOMEM; |
1557 | |
1558 | group_item->stats = netdev_alloc_pcpu_stats(struct devlink_stats); |
1559 | if (!group_item->stats) { |
1560 | err = -ENOMEM; |
1561 | goto err_stats_alloc; |
1562 | } |
1563 | |
1564 | group_item->group = group; |
1565 | |
1566 | err = devlink_trap_group_item_policer_link(devlink, group_item); |
1567 | if (err) |
1568 | goto err_policer_link; |
1569 | |
1570 | if (devlink->ops->trap_group_init) { |
1571 | err = devlink->ops->trap_group_init(devlink, group); |
1572 | if (err) |
1573 | goto err_group_init; |
1574 | } |
1575 | |
1576 | list_add_tail(new: &group_item->list, head: &devlink->trap_group_list); |
1577 | devlink_trap_group_notify(devlink, group_item, |
1578 | cmd: DEVLINK_CMD_TRAP_GROUP_NEW); |
1579 | |
1580 | return 0; |
1581 | |
1582 | err_group_init: |
1583 | err_policer_link: |
1584 | free_percpu(pdata: group_item->stats); |
1585 | err_stats_alloc: |
1586 | kfree(objp: group_item); |
1587 | return err; |
1588 | } |
1589 | |
1590 | static void |
1591 | devlink_trap_group_unregister(struct devlink *devlink, |
1592 | const struct devlink_trap_group *group) |
1593 | { |
1594 | struct devlink_trap_group_item *group_item; |
1595 | |
1596 | group_item = devlink_trap_group_item_lookup(devlink, name: group->name); |
1597 | if (WARN_ON_ONCE(!group_item)) |
1598 | return; |
1599 | |
1600 | devlink_trap_group_notify(devlink, group_item, |
1601 | cmd: DEVLINK_CMD_TRAP_GROUP_DEL); |
1602 | list_del(entry: &group_item->list); |
1603 | free_percpu(pdata: group_item->stats); |
1604 | kfree(objp: group_item); |
1605 | } |
1606 | |
1607 | /** |
1608 | * devl_trap_groups_register - Register packet trap groups with devlink. |
1609 | * @devlink: devlink. |
1610 | * @groups: Packet trap groups. |
1611 | * @groups_count: Count of provided packet trap groups. |
1612 | * |
1613 | * Return: Non-zero value on failure. |
1614 | */ |
1615 | int devl_trap_groups_register(struct devlink *devlink, |
1616 | const struct devlink_trap_group *groups, |
1617 | size_t groups_count) |
1618 | { |
1619 | int i, err; |
1620 | |
1621 | devl_assert_locked(devlink); |
1622 | for (i = 0; i < groups_count; i++) { |
1623 | const struct devlink_trap_group *group = &groups[i]; |
1624 | |
1625 | err = devlink_trap_group_verify(group); |
1626 | if (err) |
1627 | goto err_trap_group_verify; |
1628 | |
1629 | err = devlink_trap_group_register(devlink, group); |
1630 | if (err) |
1631 | goto err_trap_group_register; |
1632 | } |
1633 | |
1634 | return 0; |
1635 | |
1636 | err_trap_group_register: |
1637 | err_trap_group_verify: |
1638 | for (i--; i >= 0; i--) |
1639 | devlink_trap_group_unregister(devlink, group: &groups[i]); |
1640 | return err; |
1641 | } |
1642 | EXPORT_SYMBOL_GPL(devl_trap_groups_register); |
1643 | |
1644 | /** |
1645 | * devlink_trap_groups_register - Register packet trap groups with devlink. |
1646 | * @devlink: devlink. |
1647 | * @groups: Packet trap groups. |
1648 | * @groups_count: Count of provided packet trap groups. |
1649 | * |
1650 | * Context: Takes and release devlink->lock <mutex>. |
1651 | * |
1652 | * Return: Non-zero value on failure. |
1653 | */ |
1654 | int devlink_trap_groups_register(struct devlink *devlink, |
1655 | const struct devlink_trap_group *groups, |
1656 | size_t groups_count) |
1657 | { |
1658 | int err; |
1659 | |
1660 | devl_lock(devlink); |
1661 | err = devl_trap_groups_register(devlink, groups, groups_count); |
1662 | devl_unlock(devlink); |
1663 | return err; |
1664 | } |
1665 | EXPORT_SYMBOL_GPL(devlink_trap_groups_register); |
1666 | |
1667 | /** |
1668 | * devl_trap_groups_unregister - Unregister packet trap groups from devlink. |
1669 | * @devlink: devlink. |
1670 | * @groups: Packet trap groups. |
1671 | * @groups_count: Count of provided packet trap groups. |
1672 | */ |
1673 | void devl_trap_groups_unregister(struct devlink *devlink, |
1674 | const struct devlink_trap_group *groups, |
1675 | size_t groups_count) |
1676 | { |
1677 | int i; |
1678 | |
1679 | devl_assert_locked(devlink); |
1680 | for (i = groups_count - 1; i >= 0; i--) |
1681 | devlink_trap_group_unregister(devlink, group: &groups[i]); |
1682 | } |
1683 | EXPORT_SYMBOL_GPL(devl_trap_groups_unregister); |
1684 | |
1685 | /** |
1686 | * devlink_trap_groups_unregister - Unregister packet trap groups from devlink. |
1687 | * @devlink: devlink. |
1688 | * @groups: Packet trap groups. |
1689 | * @groups_count: Count of provided packet trap groups. |
1690 | * |
1691 | * Context: Takes and release devlink->lock <mutex>. |
1692 | */ |
1693 | void devlink_trap_groups_unregister(struct devlink *devlink, |
1694 | const struct devlink_trap_group *groups, |
1695 | size_t groups_count) |
1696 | { |
1697 | devl_lock(devlink); |
1698 | devl_trap_groups_unregister(devlink, groups, groups_count); |
1699 | devl_unlock(devlink); |
1700 | } |
1701 | EXPORT_SYMBOL_GPL(devlink_trap_groups_unregister); |
1702 | |
1703 | static void |
1704 | devlink_trap_policer_notify(struct devlink *devlink, |
1705 | const struct devlink_trap_policer_item *policer_item, |
1706 | enum devlink_command cmd) |
1707 | { |
1708 | struct sk_buff *msg; |
1709 | int err; |
1710 | |
1711 | WARN_ON_ONCE(cmd != DEVLINK_CMD_TRAP_POLICER_NEW && |
1712 | cmd != DEVLINK_CMD_TRAP_POLICER_DEL); |
1713 | if (!xa_get_mark(&devlinks, index: devlink->index, DEVLINK_REGISTERED)) |
1714 | return; |
1715 | |
1716 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1717 | if (!msg) |
1718 | return; |
1719 | |
1720 | err = devlink_nl_trap_policer_fill(msg, devlink, policer_item, cmd, portid: 0, |
1721 | seq: 0, flags: 0); |
1722 | if (err) { |
1723 | nlmsg_free(skb: msg); |
1724 | return; |
1725 | } |
1726 | |
1727 | genlmsg_multicast_netns(family: &devlink_nl_family, net: devlink_net(devlink), |
1728 | skb: msg, portid: 0, group: DEVLINK_MCGRP_CONFIG, GFP_KERNEL); |
1729 | } |
1730 | |
1731 | void devlink_trap_policers_notify_register(struct devlink *devlink) |
1732 | { |
1733 | struct devlink_trap_policer_item *policer_item; |
1734 | |
1735 | list_for_each_entry(policer_item, &devlink->trap_policer_list, list) |
1736 | devlink_trap_policer_notify(devlink, policer_item, |
1737 | cmd: DEVLINK_CMD_TRAP_POLICER_NEW); |
1738 | } |
1739 | |
1740 | void devlink_trap_policers_notify_unregister(struct devlink *devlink) |
1741 | { |
1742 | struct devlink_trap_policer_item *policer_item; |
1743 | |
1744 | list_for_each_entry_reverse(policer_item, &devlink->trap_policer_list, |
1745 | list) |
1746 | devlink_trap_policer_notify(devlink, policer_item, |
1747 | cmd: DEVLINK_CMD_TRAP_POLICER_DEL); |
1748 | } |
1749 | |
1750 | static int |
1751 | devlink_trap_policer_register(struct devlink *devlink, |
1752 | const struct devlink_trap_policer *policer) |
1753 | { |
1754 | struct devlink_trap_policer_item *policer_item; |
1755 | int err; |
1756 | |
1757 | if (devlink_trap_policer_item_lookup(devlink, id: policer->id)) |
1758 | return -EEXIST; |
1759 | |
1760 | policer_item = kzalloc(size: sizeof(*policer_item), GFP_KERNEL); |
1761 | if (!policer_item) |
1762 | return -ENOMEM; |
1763 | |
1764 | policer_item->policer = policer; |
1765 | policer_item->rate = policer->init_rate; |
1766 | policer_item->burst = policer->init_burst; |
1767 | |
1768 | if (devlink->ops->trap_policer_init) { |
1769 | err = devlink->ops->trap_policer_init(devlink, policer); |
1770 | if (err) |
1771 | goto err_policer_init; |
1772 | } |
1773 | |
1774 | list_add_tail(new: &policer_item->list, head: &devlink->trap_policer_list); |
1775 | devlink_trap_policer_notify(devlink, policer_item, |
1776 | cmd: DEVLINK_CMD_TRAP_POLICER_NEW); |
1777 | |
1778 | return 0; |
1779 | |
1780 | err_policer_init: |
1781 | kfree(objp: policer_item); |
1782 | return err; |
1783 | } |
1784 | |
1785 | static void |
1786 | devlink_trap_policer_unregister(struct devlink *devlink, |
1787 | const struct devlink_trap_policer *policer) |
1788 | { |
1789 | struct devlink_trap_policer_item *policer_item; |
1790 | |
1791 | policer_item = devlink_trap_policer_item_lookup(devlink, id: policer->id); |
1792 | if (WARN_ON_ONCE(!policer_item)) |
1793 | return; |
1794 | |
1795 | devlink_trap_policer_notify(devlink, policer_item, |
1796 | cmd: DEVLINK_CMD_TRAP_POLICER_DEL); |
1797 | list_del(entry: &policer_item->list); |
1798 | if (devlink->ops->trap_policer_fini) |
1799 | devlink->ops->trap_policer_fini(devlink, policer); |
1800 | kfree(objp: policer_item); |
1801 | } |
1802 | |
1803 | /** |
1804 | * devl_trap_policers_register - Register packet trap policers with devlink. |
1805 | * @devlink: devlink. |
1806 | * @policers: Packet trap policers. |
1807 | * @policers_count: Count of provided packet trap policers. |
1808 | * |
1809 | * Return: Non-zero value on failure. |
1810 | */ |
1811 | int |
1812 | devl_trap_policers_register(struct devlink *devlink, |
1813 | const struct devlink_trap_policer *policers, |
1814 | size_t policers_count) |
1815 | { |
1816 | int i, err; |
1817 | |
1818 | devl_assert_locked(devlink); |
1819 | for (i = 0; i < policers_count; i++) { |
1820 | const struct devlink_trap_policer *policer = &policers[i]; |
1821 | |
1822 | if (WARN_ON(policer->id == 0 || |
1823 | policer->max_rate < policer->min_rate || |
1824 | policer->max_burst < policer->min_burst)) { |
1825 | err = -EINVAL; |
1826 | goto err_trap_policer_verify; |
1827 | } |
1828 | |
1829 | err = devlink_trap_policer_register(devlink, policer); |
1830 | if (err) |
1831 | goto err_trap_policer_register; |
1832 | } |
1833 | return 0; |
1834 | |
1835 | err_trap_policer_register: |
1836 | err_trap_policer_verify: |
1837 | for (i--; i >= 0; i--) |
1838 | devlink_trap_policer_unregister(devlink, policer: &policers[i]); |
1839 | return err; |
1840 | } |
1841 | EXPORT_SYMBOL_GPL(devl_trap_policers_register); |
1842 | |
1843 | /** |
1844 | * devl_trap_policers_unregister - Unregister packet trap policers from devlink. |
1845 | * @devlink: devlink. |
1846 | * @policers: Packet trap policers. |
1847 | * @policers_count: Count of provided packet trap policers. |
1848 | */ |
1849 | void |
1850 | devl_trap_policers_unregister(struct devlink *devlink, |
1851 | const struct devlink_trap_policer *policers, |
1852 | size_t policers_count) |
1853 | { |
1854 | int i; |
1855 | |
1856 | devl_assert_locked(devlink); |
1857 | for (i = policers_count - 1; i >= 0; i--) |
1858 | devlink_trap_policer_unregister(devlink, policer: &policers[i]); |
1859 | } |
1860 | EXPORT_SYMBOL_GPL(devl_trap_policers_unregister); |
1861 | |