]>
Commit | Line | Data |
---|---|---|
4bbdec80 JP |
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, nla_data(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(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(msg, 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(msg, 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(msg, DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE)) | |
145 | goto nla_put_failure; | |
146 | ||
147 | nla_nest_end(msg, attr); | |
148 | ||
149 | return 0; | |
150 | ||
151 | nla_put_failure: | |
152 | nla_nest_cancel(msg, 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(&cpu_stats->syncp); | |
170 | rx_packets = u64_stats_read(&cpu_stats->rx_packets); | |
171 | rx_bytes = u64_stats_read(&cpu_stats->rx_bytes); | |
172 | } while (u64_stats_fetch_retry(&cpu_stats->syncp, start)); | |
173 | ||
174 | u64_stats_add(&stats->rx_packets, rx_packets); | |
175 | u64_stats_add(&stats->rx_bytes, 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); | |
187 | ||
188 | attr = nla_nest_start(msg, DEVLINK_ATTR_STATS); | |
189 | if (!attr) | |
190 | return -EMSGSIZE; | |
191 | ||
192 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_PACKETS, | |
193 | u64_stats_read(&stats.rx_packets), | |
194 | DEVLINK_ATTR_PAD)) | |
195 | goto nla_put_failure; | |
196 | ||
197 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_BYTES, | |
198 | u64_stats_read(&stats.rx_bytes), | |
199 | DEVLINK_ATTR_PAD)) | |
200 | goto nla_put_failure; | |
201 | ||
202 | nla_nest_end(msg, attr); | |
203 | ||
204 | return 0; | |
205 | ||
206 | nla_put_failure: | |
207 | nla_nest_cancel(msg, 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_item->stats, &stats); | |
228 | ||
229 | attr = nla_nest_start(msg, DEVLINK_ATTR_STATS); | |
230 | if (!attr) | |
231 | return -EMSGSIZE; | |
232 | ||
233 | if (devlink->ops->trap_drop_counter_get && | |
234 | nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_DROPPED, drops, | |
235 | DEVLINK_ATTR_PAD)) | |
236 | goto nla_put_failure; | |
237 | ||
238 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_PACKETS, | |
239 | u64_stats_read(&stats.rx_packets), | |
240 | DEVLINK_ATTR_PAD)) | |
241 | goto nla_put_failure; | |
242 | ||
243 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_BYTES, | |
244 | u64_stats_read(&stats.rx_bytes), | |
245 | DEVLINK_ATTR_PAD)) | |
246 | goto nla_put_failure; | |
247 | ||
248 | nla_nest_end(msg, attr); | |
249 | ||
250 | return 0; | |
251 | ||
252 | nla_put_failure: | |
253 | nla_nest_cancel(msg, 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(msg, portid, seq, &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(msg, DEVLINK_ATTR_TRAP_GROUP_NAME, | |
274 | group_item->group->name)) | |
275 | goto nla_put_failure; | |
276 | ||
277 | if (nla_put_string(msg, DEVLINK_ATTR_TRAP_NAME, trap_item->trap->name)) | |
278 | goto nla_put_failure; | |
279 | ||
280 | if (nla_put_u8(msg, DEVLINK_ATTR_TRAP_TYPE, trap_item->trap->type)) | |
281 | goto nla_put_failure; | |
282 | ||
283 | if (trap_item->trap->generic && | |
284 | nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC)) | |
285 | goto nla_put_failure; | |
286 | ||
287 | if (nla_put_u8(msg, DEVLINK_ATTR_TRAP_ACTION, trap_item->action)) | |
288 | goto nla_put_failure; | |
289 | ||
290 | err = devlink_trap_metadata_put(msg, 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(msg, hdr); | |
299 | ||
300 | return 0; | |
301 | ||
302 | nla_put_failure: | |
303 | genlmsg_cancel(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(&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 | DEVLINK_CMD_TRAP_NEW, info->snd_portid, | |
330 | info->snd_seq, 0); | |
331 | if (err) | |
332 | goto err_trap_fill; | |
333 | ||
334 | return genlmsg_reply(msg, info); | |
335 | ||
336 | err_trap_fill: | |
337 | nlmsg_free(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 | DEVLINK_CMD_TRAP_NEW, | |
357 | NETLINK_CB(cb->skb).portid, | |
358 | 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(skb, cb, 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, &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 | info->extack); | |
415 | } | |
416 | ||
53590934 | 417 | int devlink_nl_trap_set_doit(struct sk_buff *skb, struct genl_info *info) |
4bbdec80 JP |
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(&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(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(msg, portid, seq, &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(msg, DEVLINK_ATTR_TRAP_GROUP_NAME, | |
491 | group_item->group->name)) | |
492 | goto nla_put_failure; | |
493 | ||
494 | if (group_item->group->generic && | |
495 | nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC)) | |
496 | goto nla_put_failure; | |
497 | ||
498 | if (group_item->policer_item && | |
499 | nla_put_u32(msg, DEVLINK_ATTR_TRAP_POLICER_ID, | |
500 | group_item->policer_item->policer->id)) | |
501 | goto nla_put_failure; | |
502 | ||
503 | err = devlink_trap_group_stats_put(msg, group_item->stats); | |
504 | if (err) | |
505 | goto nla_put_failure; | |
506 | ||
507 | genlmsg_end(msg, hdr); | |
508 | ||
509 | return 0; | |
510 | ||
511 | nla_put_failure: | |
512 | genlmsg_cancel(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(&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 | DEVLINK_CMD_TRAP_GROUP_NEW, | |
539 | info->snd_portid, info->snd_seq, 0); | |
540 | if (err) | |
541 | goto err_trap_group_fill; | |
542 | ||
543 | return genlmsg_reply(msg, info); | |
544 | ||
545 | err_trap_group_fill: | |
546 | nlmsg_free(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 | DEVLINK_CMD_TRAP_GROUP_NEW, | |
567 | NETLINK_CB(cb->skb).portid, | |
568 | 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(skb, cb, 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, &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 | 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(attrs[DEVLINK_ATTR_TRAP_POLICER_ID]); | |
670 | policer_item = devlink_trap_policer_item_lookup(devlink, 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 | ||
53590934 | 687 | int devlink_nl_trap_group_set_doit(struct sk_buff *skb, struct genl_info *info) |
4bbdec80 JP |
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(&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 | &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(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(msg, DEVLINK_ATTR_STATS); | |
750 | if (!attr) | |
751 | return -EMSGSIZE; | |
752 | ||
753 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_DROPPED, drops, | |
754 | DEVLINK_ATTR_PAD)) | |
755 | goto nla_put_failure; | |
756 | ||
757 | nla_nest_end(msg, attr); | |
758 | ||
759 | return 0; | |
760 | ||
761 | nla_put_failure: | |
762 | nla_nest_cancel(msg, 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(msg, portid, seq, &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(msg, DEVLINK_ATTR_TRAP_POLICER_ID, | |
783 | policer_item->policer->id)) | |
784 | goto nla_put_failure; | |
785 | ||
786 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_TRAP_POLICER_RATE, | |
787 | policer_item->rate, DEVLINK_ATTR_PAD)) | |
788 | goto nla_put_failure; | |
789 | ||
790 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_TRAP_POLICER_BURST, | |
791 | policer_item->burst, DEVLINK_ATTR_PAD)) | |
792 | goto nla_put_failure; | |
793 | ||
794 | err = devlink_trap_policer_stats_put(msg, devlink, | |
795 | policer_item->policer); | |
796 | if (err) | |
797 | goto nla_put_failure; | |
798 | ||
799 | genlmsg_end(msg, hdr); | |
800 | ||
801 | return 0; | |
802 | ||
803 | nla_put_failure: | |
804 | genlmsg_cancel(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(&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 | DEVLINK_CMD_TRAP_POLICER_NEW, | |
832 | info->snd_portid, info->snd_seq, 0); | |
833 | if (err) | |
834 | goto err_trap_policer_fill; | |
835 | ||
836 | return genlmsg_reply(msg, info); | |
837 | ||
838 | err_trap_policer_fill: | |
839 | nlmsg_free(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 | DEVLINK_CMD_TRAP_POLICER_NEW, | |
860 | NETLINK_CB(cb->skb).portid, | |
861 | 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(skb, cb, 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(attrs[DEVLINK_ATTR_TRAP_POLICER_RATE]); | |
893 | ||
894 | if (attrs[DEVLINK_ATTR_TRAP_POLICER_BURST]) | |
895 | burst = nla_get_u64(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 | ||
53590934 JP |
928 | int devlink_nl_trap_policer_set_doit(struct sk_buff *skb, |
929 | struct genl_info *info) | |
4bbdec80 JP |
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(&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, 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, 0, 0, | |
1184 | 0); | |
1185 | if (err) { | |
1186 | nlmsg_free(msg); | |
1187 | return; | |
1188 | } | |
1189 | ||
1190 | genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), | |
1191 | msg, 0, 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 | 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 | 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, 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, 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, 0, 0, 0); | |
1245 | if (err) { | |
1246 | nlmsg_free(msg); | |
1247 | return; | |
1248 | } | |
1249 | ||
1250 | genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), | |
1251 | msg, 0, 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, 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, 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, trap->name)) | |
1278 | return -EEXIST; | |
1279 | ||
1280 | trap_item = kzalloc(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(&trap_item->list, &devlink->trap_list); | |
1303 | devlink_trap_notify(devlink, trap_item, DEVLINK_CMD_TRAP_NEW); | |
1304 | ||
1305 | return 0; | |
1306 | ||
1307 | err_trap_init: | |
1308 | err_group_link: | |
1309 | free_percpu(trap_item->stats); | |
1310 | err_stats_alloc: | |
1311 | kfree(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, trap->name); | |
1321 | if (WARN_ON_ONCE(!trap_item)) | |
1322 | return; | |
1323 | ||
1324 | devlink_trap_notify(devlink, trap_item, DEVLINK_CMD_TRAP_DEL); | |
1325 | list_del(&trap_item->list); | |
1326 | if (devlink->ops->trap_fini) | |
1327 | devlink->ops->trap_fini(devlink, trap, trap_item); | |
1328 | free_percpu(trap_item->stats); | |
1329 | kfree(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, 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, &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, &traps[i]); | |
1429 | synchronize_rcu(); | |
1430 | for (i = traps_count - 1; i >= 0; i--) | |
1431 | devlink_trap_unregister(devlink, &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(&stats->syncp); | |
1461 | u64_stats_add(&stats->rx_bytes, skb_len); | |
1462 | u64_stats_inc(&stats->rx_packets); | |
1463 | u64_stats_update_end(&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(&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(&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_item->stats, skb->len); | |
1499 | devlink_trap_stats_update(trap_item->group_item->stats, skb->len); | |
1500 | ||
1501 | if (tracepoint_enabled(devlink_trap_report)) { | |
1502 | struct devlink_trap_metadata metadata = {}; | |
1503 | ||
1504 | devlink_trap_report_metadata_set(&metadata, trap_item, | |
1505 | in_devlink_port, fa_cookie); | |
1506 | trace_devlink_trap_report(devlink, skb, &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, 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, group->name)) | |
1552 | return -EEXIST; | |
1553 | ||
1554 | group_item = kzalloc(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(&group_item->list, &devlink->trap_group_list); | |
1577 | devlink_trap_group_notify(devlink, group_item, | |
1578 | DEVLINK_CMD_TRAP_GROUP_NEW); | |
1579 | ||
1580 | return 0; | |
1581 | ||
1582 | err_group_init: | |
1583 | err_policer_link: | |
1584 | free_percpu(group_item->stats); | |
1585 | err_stats_alloc: | |
1586 | kfree(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, group->name); | |
1597 | if (WARN_ON_ONCE(!group_item)) | |
1598 | return; | |
1599 | ||
1600 | devlink_trap_group_notify(devlink, group_item, | |
1601 | DEVLINK_CMD_TRAP_GROUP_DEL); | |
1602 | list_del(&group_item->list); | |
1603 | free_percpu(group_item->stats); | |
1604 | kfree(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, &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, &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, 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, 0, | |
1721 | 0, 0); | |
1722 | if (err) { | |
1723 | nlmsg_free(msg); | |
1724 | return; | |
1725 | } | |
1726 | ||
1727 | genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), | |
1728 | msg, 0, 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 | 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 | 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, policer->id)) | |
1758 | return -EEXIST; | |
1759 | ||
1760 | policer_item = kzalloc(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(&policer_item->list, &devlink->trap_policer_list); | |
1775 | devlink_trap_policer_notify(devlink, policer_item, | |
1776 | DEVLINK_CMD_TRAP_POLICER_NEW); | |
1777 | ||
1778 | return 0; | |
1779 | ||
1780 | err_policer_init: | |
1781 | kfree(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, policer->id); | |
1792 | if (WARN_ON_ONCE(!policer_item)) | |
1793 | return; | |
1794 | ||
1795 | devlink_trap_policer_notify(devlink, policer_item, | |
1796 | DEVLINK_CMD_TRAP_POLICER_DEL); | |
1797 | list_del(&policer_item->list); | |
1798 | if (devlink->ops->trap_policer_fini) | |
1799 | devlink->ops->trap_policer_fini(devlink, policer); | |
1800 | kfree(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, &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, &policers[i]); | |
1859 | } | |
1860 | EXPORT_SYMBOL_GPL(devl_trap_policers_unregister); |