diff options
Diffstat (limited to 'net/core/devlink.c')
-rw-r--r-- | net/core/devlink.c | 1080 |
1 files changed, 1075 insertions, 5 deletions
diff --git a/net/core/devlink.c b/net/core/devlink.c index d3dbb904bf3b..650f36379203 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/spinlock.h> | 18 | #include <linux/spinlock.h> |
19 | #include <linux/refcount.h> | 19 | #include <linux/refcount.h> |
20 | #include <linux/workqueue.h> | 20 | #include <linux/workqueue.h> |
21 | #include <linux/u64_stats_sync.h> | ||
22 | #include <linux/timekeeping.h> | ||
21 | #include <rdma/ib_verbs.h> | 23 | #include <rdma/ib_verbs.h> |
22 | #include <net/netlink.h> | 24 | #include <net/netlink.h> |
23 | #include <net/genetlink.h> | 25 | #include <net/genetlink.h> |
@@ -25,6 +27,7 @@ | |||
25 | #include <net/net_namespace.h> | 27 | #include <net/net_namespace.h> |
26 | #include <net/sock.h> | 28 | #include <net/sock.h> |
27 | #include <net/devlink.h> | 29 | #include <net/devlink.h> |
30 | #include <net/drop_monitor.h> | ||
28 | #define CREATE_TRACE_POINTS | 31 | #define CREATE_TRACE_POINTS |
29 | #include <trace/events/devlink.h> | 32 | #include <trace/events/devlink.h> |
30 | 33 | ||
@@ -551,7 +554,7 @@ static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink, | |||
551 | if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) | 554 | if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) |
552 | goto nla_put_failure; | 555 | goto nla_put_failure; |
553 | 556 | ||
554 | spin_lock(&devlink_port->type_lock); | 557 | spin_lock_bh(&devlink_port->type_lock); |
555 | if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type)) | 558 | if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type)) |
556 | goto nla_put_failure_type_locked; | 559 | goto nla_put_failure_type_locked; |
557 | if (devlink_port->desired_type != DEVLINK_PORT_TYPE_NOTSET && | 560 | if (devlink_port->desired_type != DEVLINK_PORT_TYPE_NOTSET && |
@@ -576,7 +579,7 @@ static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink, | |||
576 | ibdev->name)) | 579 | ibdev->name)) |
577 | goto nla_put_failure_type_locked; | 580 | goto nla_put_failure_type_locked; |
578 | } | 581 | } |
579 | spin_unlock(&devlink_port->type_lock); | 582 | spin_unlock_bh(&devlink_port->type_lock); |
580 | if (devlink_nl_port_attrs_put(msg, devlink_port)) | 583 | if (devlink_nl_port_attrs_put(msg, devlink_port)) |
581 | goto nla_put_failure; | 584 | goto nla_put_failure; |
582 | 585 | ||
@@ -584,7 +587,7 @@ static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink, | |||
584 | return 0; | 587 | return 0; |
585 | 588 | ||
586 | nla_put_failure_type_locked: | 589 | nla_put_failure_type_locked: |
587 | spin_unlock(&devlink_port->type_lock); | 590 | spin_unlock_bh(&devlink_port->type_lock); |
588 | nla_put_failure: | 591 | nla_put_failure: |
589 | genlmsg_cancel(msg, hdr); | 592 | genlmsg_cancel(msg, hdr); |
590 | return -EMSGSIZE; | 593 | return -EMSGSIZE; |
@@ -5154,6 +5157,571 @@ devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb, | |||
5154 | return 0; | 5157 | return 0; |
5155 | } | 5158 | } |
5156 | 5159 | ||
5160 | struct devlink_stats { | ||
5161 | u64 rx_bytes; | ||
5162 | u64 rx_packets; | ||
5163 | struct u64_stats_sync syncp; | ||
5164 | }; | ||
5165 | |||
5166 | /** | ||
5167 | * struct devlink_trap_group_item - Packet trap group attributes. | ||
5168 | * @group: Immutable packet trap group attributes. | ||
5169 | * @refcount: Number of trap items using the group. | ||
5170 | * @list: trap_group_list member. | ||
5171 | * @stats: Trap group statistics. | ||
5172 | * | ||
5173 | * Describes packet trap group attributes. Created by devlink during trap | ||
5174 | * registration. | ||
5175 | */ | ||
5176 | struct devlink_trap_group_item { | ||
5177 | const struct devlink_trap_group *group; | ||
5178 | refcount_t refcount; | ||
5179 | struct list_head list; | ||
5180 | struct devlink_stats __percpu *stats; | ||
5181 | }; | ||
5182 | |||
5183 | /** | ||
5184 | * struct devlink_trap_item - Packet trap attributes. | ||
5185 | * @trap: Immutable packet trap attributes. | ||
5186 | * @group_item: Associated group item. | ||
5187 | * @list: trap_list member. | ||
5188 | * @action: Trap action. | ||
5189 | * @stats: Trap statistics. | ||
5190 | * @priv: Driver private information. | ||
5191 | * | ||
5192 | * Describes both mutable and immutable packet trap attributes. Created by | ||
5193 | * devlink during trap registration and used for all trap related operations. | ||
5194 | */ | ||
5195 | struct devlink_trap_item { | ||
5196 | const struct devlink_trap *trap; | ||
5197 | struct devlink_trap_group_item *group_item; | ||
5198 | struct list_head list; | ||
5199 | enum devlink_trap_action action; | ||
5200 | struct devlink_stats __percpu *stats; | ||
5201 | void *priv; | ||
5202 | }; | ||
5203 | |||
5204 | static struct devlink_trap_item * | ||
5205 | devlink_trap_item_lookup(struct devlink *devlink, const char *name) | ||
5206 | { | ||
5207 | struct devlink_trap_item *trap_item; | ||
5208 | |||
5209 | list_for_each_entry(trap_item, &devlink->trap_list, list) { | ||
5210 | if (!strcmp(trap_item->trap->name, name)) | ||
5211 | return trap_item; | ||
5212 | } | ||
5213 | |||
5214 | return NULL; | ||
5215 | } | ||
5216 | |||
5217 | static struct devlink_trap_item * | ||
5218 | devlink_trap_item_get_from_info(struct devlink *devlink, | ||
5219 | struct genl_info *info) | ||
5220 | { | ||
5221 | struct nlattr *attr; | ||
5222 | |||
5223 | if (!info->attrs[DEVLINK_ATTR_TRAP_NAME]) | ||
5224 | return NULL; | ||
5225 | attr = info->attrs[DEVLINK_ATTR_TRAP_NAME]; | ||
5226 | |||
5227 | return devlink_trap_item_lookup(devlink, nla_data(attr)); | ||
5228 | } | ||
5229 | |||
5230 | static int | ||
5231 | devlink_trap_action_get_from_info(struct genl_info *info, | ||
5232 | enum devlink_trap_action *p_trap_action) | ||
5233 | { | ||
5234 | u8 val; | ||
5235 | |||
5236 | val = nla_get_u8(info->attrs[DEVLINK_ATTR_TRAP_ACTION]); | ||
5237 | switch (val) { | ||
5238 | case DEVLINK_TRAP_ACTION_DROP: /* fall-through */ | ||
5239 | case DEVLINK_TRAP_ACTION_TRAP: | ||
5240 | *p_trap_action = val; | ||
5241 | break; | ||
5242 | default: | ||
5243 | return -EINVAL; | ||
5244 | } | ||
5245 | |||
5246 | return 0; | ||
5247 | } | ||
5248 | |||
5249 | static int devlink_trap_metadata_put(struct sk_buff *msg, | ||
5250 | const struct devlink_trap *trap) | ||
5251 | { | ||
5252 | struct nlattr *attr; | ||
5253 | |||
5254 | attr = nla_nest_start(msg, DEVLINK_ATTR_TRAP_METADATA); | ||
5255 | if (!attr) | ||
5256 | return -EMSGSIZE; | ||
5257 | |||
5258 | if ((trap->metadata_cap & DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT) && | ||
5259 | nla_put_flag(msg, DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT)) | ||
5260 | goto nla_put_failure; | ||
5261 | |||
5262 | nla_nest_end(msg, attr); | ||
5263 | |||
5264 | return 0; | ||
5265 | |||
5266 | nla_put_failure: | ||
5267 | nla_nest_cancel(msg, attr); | ||
5268 | return -EMSGSIZE; | ||
5269 | } | ||
5270 | |||
5271 | static void devlink_trap_stats_read(struct devlink_stats __percpu *trap_stats, | ||
5272 | struct devlink_stats *stats) | ||
5273 | { | ||
5274 | int i; | ||
5275 | |||
5276 | memset(stats, 0, sizeof(*stats)); | ||
5277 | for_each_possible_cpu(i) { | ||
5278 | struct devlink_stats *cpu_stats; | ||
5279 | u64 rx_packets, rx_bytes; | ||
5280 | unsigned int start; | ||
5281 | |||
5282 | cpu_stats = per_cpu_ptr(trap_stats, i); | ||
5283 | do { | ||
5284 | start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); | ||
5285 | rx_packets = cpu_stats->rx_packets; | ||
5286 | rx_bytes = cpu_stats->rx_bytes; | ||
5287 | } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); | ||
5288 | |||
5289 | stats->rx_packets += rx_packets; | ||
5290 | stats->rx_bytes += rx_bytes; | ||
5291 | } | ||
5292 | } | ||
5293 | |||
5294 | static int devlink_trap_stats_put(struct sk_buff *msg, | ||
5295 | struct devlink_stats __percpu *trap_stats) | ||
5296 | { | ||
5297 | struct devlink_stats stats; | ||
5298 | struct nlattr *attr; | ||
5299 | |||
5300 | devlink_trap_stats_read(trap_stats, &stats); | ||
5301 | |||
5302 | attr = nla_nest_start(msg, DEVLINK_ATTR_STATS); | ||
5303 | if (!attr) | ||
5304 | return -EMSGSIZE; | ||
5305 | |||
5306 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_PACKETS, | ||
5307 | stats.rx_packets, DEVLINK_ATTR_PAD)) | ||
5308 | goto nla_put_failure; | ||
5309 | |||
5310 | if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_BYTES, | ||
5311 | stats.rx_bytes, DEVLINK_ATTR_PAD)) | ||
5312 | goto nla_put_failure; | ||
5313 | |||
5314 | nla_nest_end(msg, attr); | ||
5315 | |||
5316 | return 0; | ||
5317 | |||
5318 | nla_put_failure: | ||
5319 | nla_nest_cancel(msg, attr); | ||
5320 | return -EMSGSIZE; | ||
5321 | } | ||
5322 | |||
5323 | static int devlink_nl_trap_fill(struct sk_buff *msg, struct devlink *devlink, | ||
5324 | const struct devlink_trap_item *trap_item, | ||
5325 | enum devlink_command cmd, u32 portid, u32 seq, | ||
5326 | int flags) | ||
5327 | { | ||
5328 | struct devlink_trap_group_item *group_item = trap_item->group_item; | ||
5329 | void *hdr; | ||
5330 | int err; | ||
5331 | |||
5332 | hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); | ||
5333 | if (!hdr) | ||
5334 | return -EMSGSIZE; | ||
5335 | |||
5336 | if (devlink_nl_put_handle(msg, devlink)) | ||
5337 | goto nla_put_failure; | ||
5338 | |||
5339 | if (nla_put_string(msg, DEVLINK_ATTR_TRAP_GROUP_NAME, | ||
5340 | group_item->group->name)) | ||
5341 | goto nla_put_failure; | ||
5342 | |||
5343 | if (nla_put_string(msg, DEVLINK_ATTR_TRAP_NAME, trap_item->trap->name)) | ||
5344 | goto nla_put_failure; | ||
5345 | |||
5346 | if (nla_put_u8(msg, DEVLINK_ATTR_TRAP_TYPE, trap_item->trap->type)) | ||
5347 | goto nla_put_failure; | ||
5348 | |||
5349 | if (trap_item->trap->generic && | ||
5350 | nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC)) | ||
5351 | goto nla_put_failure; | ||
5352 | |||
5353 | if (nla_put_u8(msg, DEVLINK_ATTR_TRAP_ACTION, trap_item->action)) | ||
5354 | goto nla_put_failure; | ||
5355 | |||
5356 | err = devlink_trap_metadata_put(msg, trap_item->trap); | ||
5357 | if (err) | ||
5358 | goto nla_put_failure; | ||
5359 | |||
5360 | err = devlink_trap_stats_put(msg, trap_item->stats); | ||
5361 | if (err) | ||
5362 | goto nla_put_failure; | ||
5363 | |||
5364 | genlmsg_end(msg, hdr); | ||
5365 | |||
5366 | return 0; | ||
5367 | |||
5368 | nla_put_failure: | ||
5369 | genlmsg_cancel(msg, hdr); | ||
5370 | return -EMSGSIZE; | ||
5371 | } | ||
5372 | |||
5373 | static int devlink_nl_cmd_trap_get_doit(struct sk_buff *skb, | ||
5374 | struct genl_info *info) | ||
5375 | { | ||
5376 | struct netlink_ext_ack *extack = info->extack; | ||
5377 | struct devlink *devlink = info->user_ptr[0]; | ||
5378 | struct devlink_trap_item *trap_item; | ||
5379 | struct sk_buff *msg; | ||
5380 | int err; | ||
5381 | |||
5382 | if (list_empty(&devlink->trap_list)) | ||
5383 | return -EOPNOTSUPP; | ||
5384 | |||
5385 | trap_item = devlink_trap_item_get_from_info(devlink, info); | ||
5386 | if (!trap_item) { | ||
5387 | NL_SET_ERR_MSG_MOD(extack, "Device did not register this trap"); | ||
5388 | return -ENOENT; | ||
5389 | } | ||
5390 | |||
5391 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
5392 | if (!msg) | ||
5393 | return -ENOMEM; | ||
5394 | |||
5395 | err = devlink_nl_trap_fill(msg, devlink, trap_item, | ||
5396 | DEVLINK_CMD_TRAP_NEW, info->snd_portid, | ||
5397 | info->snd_seq, 0); | ||
5398 | if (err) | ||
5399 | goto err_trap_fill; | ||
5400 | |||
5401 | return genlmsg_reply(msg, info); | ||
5402 | |||
5403 | err_trap_fill: | ||
5404 | nlmsg_free(msg); | ||
5405 | return err; | ||
5406 | } | ||
5407 | |||
5408 | static int devlink_nl_cmd_trap_get_dumpit(struct sk_buff *msg, | ||
5409 | struct netlink_callback *cb) | ||
5410 | { | ||
5411 | struct devlink_trap_item *trap_item; | ||
5412 | struct devlink *devlink; | ||
5413 | int start = cb->args[0]; | ||
5414 | int idx = 0; | ||
5415 | int err; | ||
5416 | |||
5417 | mutex_lock(&devlink_mutex); | ||
5418 | list_for_each_entry(devlink, &devlink_list, list) { | ||
5419 | if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) | ||
5420 | continue; | ||
5421 | mutex_lock(&devlink->lock); | ||
5422 | list_for_each_entry(trap_item, &devlink->trap_list, list) { | ||
5423 | if (idx < start) { | ||
5424 | idx++; | ||
5425 | continue; | ||
5426 | } | ||
5427 | err = devlink_nl_trap_fill(msg, devlink, trap_item, | ||
5428 | DEVLINK_CMD_TRAP_NEW, | ||
5429 | NETLINK_CB(cb->skb).portid, | ||
5430 | cb->nlh->nlmsg_seq, | ||
5431 | NLM_F_MULTI); | ||
5432 | if (err) { | ||
5433 | mutex_unlock(&devlink->lock); | ||
5434 | goto out; | ||
5435 | } | ||
5436 | idx++; | ||
5437 | } | ||
5438 | mutex_unlock(&devlink->lock); | ||
5439 | } | ||
5440 | out: | ||
5441 | mutex_unlock(&devlink_mutex); | ||
5442 | |||
5443 | cb->args[0] = idx; | ||
5444 | return msg->len; | ||
5445 | } | ||
5446 | |||
5447 | static int __devlink_trap_action_set(struct devlink *devlink, | ||
5448 | struct devlink_trap_item *trap_item, | ||
5449 | enum devlink_trap_action trap_action, | ||
5450 | struct netlink_ext_ack *extack) | ||
5451 | { | ||
5452 | int err; | ||
5453 | |||
5454 | if (trap_item->action != trap_action && | ||
5455 | trap_item->trap->type != DEVLINK_TRAP_TYPE_DROP) { | ||
5456 | NL_SET_ERR_MSG_MOD(extack, "Cannot change action of non-drop traps. Skipping"); | ||
5457 | return 0; | ||
5458 | } | ||
5459 | |||
5460 | err = devlink->ops->trap_action_set(devlink, trap_item->trap, | ||
5461 | trap_action); | ||
5462 | if (err) | ||
5463 | return err; | ||
5464 | |||
5465 | trap_item->action = trap_action; | ||
5466 | |||
5467 | return 0; | ||
5468 | } | ||
5469 | |||
5470 | static int devlink_trap_action_set(struct devlink *devlink, | ||
5471 | struct devlink_trap_item *trap_item, | ||
5472 | struct genl_info *info) | ||
5473 | { | ||
5474 | enum devlink_trap_action trap_action; | ||
5475 | int err; | ||
5476 | |||
5477 | if (!info->attrs[DEVLINK_ATTR_TRAP_ACTION]) | ||
5478 | return 0; | ||
5479 | |||
5480 | err = devlink_trap_action_get_from_info(info, &trap_action); | ||
5481 | if (err) { | ||
5482 | NL_SET_ERR_MSG_MOD(info->extack, "Invalid trap action"); | ||
5483 | return -EINVAL; | ||
5484 | } | ||
5485 | |||
5486 | return __devlink_trap_action_set(devlink, trap_item, trap_action, | ||
5487 | info->extack); | ||
5488 | } | ||
5489 | |||
5490 | static int devlink_nl_cmd_trap_set_doit(struct sk_buff *skb, | ||
5491 | struct genl_info *info) | ||
5492 | { | ||
5493 | struct netlink_ext_ack *extack = info->extack; | ||
5494 | struct devlink *devlink = info->user_ptr[0]; | ||
5495 | struct devlink_trap_item *trap_item; | ||
5496 | int err; | ||
5497 | |||
5498 | if (list_empty(&devlink->trap_list)) | ||
5499 | return -EOPNOTSUPP; | ||
5500 | |||
5501 | trap_item = devlink_trap_item_get_from_info(devlink, info); | ||
5502 | if (!trap_item) { | ||
5503 | NL_SET_ERR_MSG_MOD(extack, "Device did not register this trap"); | ||
5504 | return -ENOENT; | ||
5505 | } | ||
5506 | |||
5507 | err = devlink_trap_action_set(devlink, trap_item, info); | ||
5508 | if (err) | ||
5509 | return err; | ||
5510 | |||
5511 | return 0; | ||
5512 | } | ||
5513 | |||
5514 | static struct devlink_trap_group_item * | ||
5515 | devlink_trap_group_item_lookup(struct devlink *devlink, const char *name) | ||
5516 | { | ||
5517 | struct devlink_trap_group_item *group_item; | ||
5518 | |||
5519 | list_for_each_entry(group_item, &devlink->trap_group_list, list) { | ||
5520 | if (!strcmp(group_item->group->name, name)) | ||
5521 | return group_item; | ||
5522 | } | ||
5523 | |||
5524 | return NULL; | ||
5525 | } | ||
5526 | |||
5527 | static struct devlink_trap_group_item * | ||
5528 | devlink_trap_group_item_get_from_info(struct devlink *devlink, | ||
5529 | struct genl_info *info) | ||
5530 | { | ||
5531 | char *name; | ||
5532 | |||
5533 | if (!info->attrs[DEVLINK_ATTR_TRAP_GROUP_NAME]) | ||
5534 | return NULL; | ||
5535 | name = nla_data(info->attrs[DEVLINK_ATTR_TRAP_GROUP_NAME]); | ||
5536 | |||
5537 | return devlink_trap_group_item_lookup(devlink, name); | ||
5538 | } | ||
5539 | |||
5540 | static int | ||
5541 | devlink_nl_trap_group_fill(struct sk_buff *msg, struct devlink *devlink, | ||
5542 | const struct devlink_trap_group_item *group_item, | ||
5543 | enum devlink_command cmd, u32 portid, u32 seq, | ||
5544 | int flags) | ||
5545 | { | ||
5546 | void *hdr; | ||
5547 | int err; | ||
5548 | |||
5549 | hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); | ||
5550 | if (!hdr) | ||
5551 | return -EMSGSIZE; | ||
5552 | |||
5553 | if (devlink_nl_put_handle(msg, devlink)) | ||
5554 | goto nla_put_failure; | ||
5555 | |||
5556 | if (nla_put_string(msg, DEVLINK_ATTR_TRAP_GROUP_NAME, | ||
5557 | group_item->group->name)) | ||
5558 | goto nla_put_failure; | ||
5559 | |||
5560 | if (group_item->group->generic && | ||
5561 | nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC)) | ||
5562 | goto nla_put_failure; | ||
5563 | |||
5564 | err = devlink_trap_stats_put(msg, group_item->stats); | ||
5565 | if (err) | ||
5566 | goto nla_put_failure; | ||
5567 | |||
5568 | genlmsg_end(msg, hdr); | ||
5569 | |||
5570 | return 0; | ||
5571 | |||
5572 | nla_put_failure: | ||
5573 | genlmsg_cancel(msg, hdr); | ||
5574 | return -EMSGSIZE; | ||
5575 | } | ||
5576 | |||
5577 | static int devlink_nl_cmd_trap_group_get_doit(struct sk_buff *skb, | ||
5578 | struct genl_info *info) | ||
5579 | { | ||
5580 | struct netlink_ext_ack *extack = info->extack; | ||
5581 | struct devlink *devlink = info->user_ptr[0]; | ||
5582 | struct devlink_trap_group_item *group_item; | ||
5583 | struct sk_buff *msg; | ||
5584 | int err; | ||
5585 | |||
5586 | if (list_empty(&devlink->trap_group_list)) | ||
5587 | return -EOPNOTSUPP; | ||
5588 | |||
5589 | group_item = devlink_trap_group_item_get_from_info(devlink, info); | ||
5590 | if (!group_item) { | ||
5591 | NL_SET_ERR_MSG_MOD(extack, "Device did not register this trap group"); | ||
5592 | return -ENOENT; | ||
5593 | } | ||
5594 | |||
5595 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
5596 | if (!msg) | ||
5597 | return -ENOMEM; | ||
5598 | |||
5599 | err = devlink_nl_trap_group_fill(msg, devlink, group_item, | ||
5600 | DEVLINK_CMD_TRAP_GROUP_NEW, | ||
5601 | info->snd_portid, info->snd_seq, 0); | ||
5602 | if (err) | ||
5603 | goto err_trap_group_fill; | ||
5604 | |||
5605 | return genlmsg_reply(msg, info); | ||
5606 | |||
5607 | err_trap_group_fill: | ||
5608 | nlmsg_free(msg); | ||
5609 | return err; | ||
5610 | } | ||
5611 | |||
5612 | static int devlink_nl_cmd_trap_group_get_dumpit(struct sk_buff *msg, | ||
5613 | struct netlink_callback *cb) | ||
5614 | { | ||
5615 | enum devlink_command cmd = DEVLINK_CMD_TRAP_GROUP_NEW; | ||
5616 | struct devlink_trap_group_item *group_item; | ||
5617 | u32 portid = NETLINK_CB(cb->skb).portid; | ||
5618 | struct devlink *devlink; | ||
5619 | int start = cb->args[0]; | ||
5620 | int idx = 0; | ||
5621 | int err; | ||
5622 | |||
5623 | mutex_lock(&devlink_mutex); | ||
5624 | list_for_each_entry(devlink, &devlink_list, list) { | ||
5625 | if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) | ||
5626 | continue; | ||
5627 | mutex_lock(&devlink->lock); | ||
5628 | list_for_each_entry(group_item, &devlink->trap_group_list, | ||
5629 | list) { | ||
5630 | if (idx < start) { | ||
5631 | idx++; | ||
5632 | continue; | ||
5633 | } | ||
5634 | err = devlink_nl_trap_group_fill(msg, devlink, | ||
5635 | group_item, cmd, | ||
5636 | portid, | ||
5637 | cb->nlh->nlmsg_seq, | ||
5638 | NLM_F_MULTI); | ||
5639 | if (err) { | ||
5640 | mutex_unlock(&devlink->lock); | ||
5641 | goto out; | ||
5642 | } | ||
5643 | idx++; | ||
5644 | } | ||
5645 | mutex_unlock(&devlink->lock); | ||
5646 | } | ||
5647 | out: | ||
5648 | mutex_unlock(&devlink_mutex); | ||
5649 | |||
5650 | cb->args[0] = idx; | ||
5651 | return msg->len; | ||
5652 | } | ||
5653 | |||
5654 | static int | ||
5655 | __devlink_trap_group_action_set(struct devlink *devlink, | ||
5656 | struct devlink_trap_group_item *group_item, | ||
5657 | enum devlink_trap_action trap_action, | ||
5658 | struct netlink_ext_ack *extack) | ||
5659 | { | ||
5660 | const char *group_name = group_item->group->name; | ||
5661 | struct devlink_trap_item *trap_item; | ||
5662 | int err; | ||
5663 | |||
5664 | list_for_each_entry(trap_item, &devlink->trap_list, list) { | ||
5665 | if (strcmp(trap_item->trap->group.name, group_name)) | ||
5666 | continue; | ||
5667 | err = __devlink_trap_action_set(devlink, trap_item, | ||
5668 | trap_action, extack); | ||
5669 | if (err) | ||
5670 | return err; | ||
5671 | } | ||
5672 | |||
5673 | return 0; | ||
5674 | } | ||
5675 | |||
5676 | static int | ||
5677 | devlink_trap_group_action_set(struct devlink *devlink, | ||
5678 | struct devlink_trap_group_item *group_item, | ||
5679 | struct genl_info *info) | ||
5680 | { | ||
5681 | enum devlink_trap_action trap_action; | ||
5682 | int err; | ||
5683 | |||
5684 | if (!info->attrs[DEVLINK_ATTR_TRAP_ACTION]) | ||
5685 | return 0; | ||
5686 | |||
5687 | err = devlink_trap_action_get_from_info(info, &trap_action); | ||
5688 | if (err) { | ||
5689 | NL_SET_ERR_MSG_MOD(info->extack, "Invalid trap action"); | ||
5690 | return -EINVAL; | ||
5691 | } | ||
5692 | |||
5693 | err = __devlink_trap_group_action_set(devlink, group_item, trap_action, | ||
5694 | info->extack); | ||
5695 | if (err) | ||
5696 | return err; | ||
5697 | |||
5698 | return 0; | ||
5699 | } | ||
5700 | |||
5701 | static int devlink_nl_cmd_trap_group_set_doit(struct sk_buff *skb, | ||
5702 | struct genl_info *info) | ||
5703 | { | ||
5704 | struct netlink_ext_ack *extack = info->extack; | ||
5705 | struct devlink *devlink = info->user_ptr[0]; | ||
5706 | struct devlink_trap_group_item *group_item; | ||
5707 | int err; | ||
5708 | |||
5709 | if (list_empty(&devlink->trap_group_list)) | ||
5710 | return -EOPNOTSUPP; | ||
5711 | |||
5712 | group_item = devlink_trap_group_item_get_from_info(devlink, info); | ||
5713 | if (!group_item) { | ||
5714 | NL_SET_ERR_MSG_MOD(extack, "Device did not register this trap group"); | ||
5715 | return -ENOENT; | ||
5716 | } | ||
5717 | |||
5718 | err = devlink_trap_group_action_set(devlink, group_item, info); | ||
5719 | if (err) | ||
5720 | return err; | ||
5721 | |||
5722 | return 0; | ||
5723 | } | ||
5724 | |||
5157 | static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { | 5725 | static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { |
5158 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, | 5726 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, |
5159 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, | 5727 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, |
@@ -5184,6 +5752,9 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { | |||
5184 | [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 }, | 5752 | [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 }, |
5185 | [DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING }, | 5753 | [DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING }, |
5186 | [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING }, | 5754 | [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING }, |
5755 | [DEVLINK_ATTR_TRAP_NAME] = { .type = NLA_NUL_STRING }, | ||
5756 | [DEVLINK_ATTR_TRAP_ACTION] = { .type = NLA_U8 }, | ||
5757 | [DEVLINK_ATTR_TRAP_GROUP_NAME] = { .type = NLA_NUL_STRING }, | ||
5187 | }; | 5758 | }; |
5188 | 5759 | ||
5189 | static const struct genl_ops devlink_nl_ops[] = { | 5760 | static const struct genl_ops devlink_nl_ops[] = { |
@@ -5483,6 +6054,32 @@ static const struct genl_ops devlink_nl_ops[] = { | |||
5483 | .flags = GENL_ADMIN_PERM, | 6054 | .flags = GENL_ADMIN_PERM, |
5484 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | 6055 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, |
5485 | }, | 6056 | }, |
6057 | { | ||
6058 | .cmd = DEVLINK_CMD_TRAP_GET, | ||
6059 | .doit = devlink_nl_cmd_trap_get_doit, | ||
6060 | .dumpit = devlink_nl_cmd_trap_get_dumpit, | ||
6061 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | ||
6062 | /* can be retrieved by unprivileged users */ | ||
6063 | }, | ||
6064 | { | ||
6065 | .cmd = DEVLINK_CMD_TRAP_SET, | ||
6066 | .doit = devlink_nl_cmd_trap_set_doit, | ||
6067 | .flags = GENL_ADMIN_PERM, | ||
6068 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | ||
6069 | }, | ||
6070 | { | ||
6071 | .cmd = DEVLINK_CMD_TRAP_GROUP_GET, | ||
6072 | .doit = devlink_nl_cmd_trap_group_get_doit, | ||
6073 | .dumpit = devlink_nl_cmd_trap_group_get_dumpit, | ||
6074 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | ||
6075 | /* can be retrieved by unprivileged users */ | ||
6076 | }, | ||
6077 | { | ||
6078 | .cmd = DEVLINK_CMD_TRAP_GROUP_SET, | ||
6079 | .doit = devlink_nl_cmd_trap_group_set_doit, | ||
6080 | .flags = GENL_ADMIN_PERM, | ||
6081 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | ||
6082 | }, | ||
5486 | }; | 6083 | }; |
5487 | 6084 | ||
5488 | static struct genl_family devlink_nl_family __ro_after_init = { | 6085 | static struct genl_family devlink_nl_family __ro_after_init = { |
@@ -5528,6 +6125,8 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size) | |||
5528 | INIT_LIST_HEAD(&devlink->param_list); | 6125 | INIT_LIST_HEAD(&devlink->param_list); |
5529 | INIT_LIST_HEAD(&devlink->region_list); | 6126 | INIT_LIST_HEAD(&devlink->region_list); |
5530 | INIT_LIST_HEAD(&devlink->reporter_list); | 6127 | INIT_LIST_HEAD(&devlink->reporter_list); |
6128 | INIT_LIST_HEAD(&devlink->trap_list); | ||
6129 | INIT_LIST_HEAD(&devlink->trap_group_list); | ||
5531 | mutex_init(&devlink->lock); | 6130 | mutex_init(&devlink->lock); |
5532 | mutex_init(&devlink->reporters_lock); | 6131 | mutex_init(&devlink->reporters_lock); |
5533 | return devlink; | 6132 | return devlink; |
@@ -5574,6 +6173,8 @@ void devlink_free(struct devlink *devlink) | |||
5574 | { | 6173 | { |
5575 | mutex_destroy(&devlink->reporters_lock); | 6174 | mutex_destroy(&devlink->reporters_lock); |
5576 | mutex_destroy(&devlink->lock); | 6175 | mutex_destroy(&devlink->lock); |
6176 | WARN_ON(!list_empty(&devlink->trap_group_list)); | ||
6177 | WARN_ON(!list_empty(&devlink->trap_list)); | ||
5577 | WARN_ON(!list_empty(&devlink->reporter_list)); | 6178 | WARN_ON(!list_empty(&devlink->reporter_list)); |
5578 | WARN_ON(!list_empty(&devlink->region_list)); | 6179 | WARN_ON(!list_empty(&devlink->region_list)); |
5579 | WARN_ON(!list_empty(&devlink->param_list)); | 6180 | WARN_ON(!list_empty(&devlink->param_list)); |
@@ -5678,10 +6279,10 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port, | |||
5678 | if (WARN_ON(!devlink_port->registered)) | 6279 | if (WARN_ON(!devlink_port->registered)) |
5679 | return; | 6280 | return; |
5680 | devlink_port_type_warn_cancel(devlink_port); | 6281 | devlink_port_type_warn_cancel(devlink_port); |
5681 | spin_lock(&devlink_port->type_lock); | 6282 | spin_lock_bh(&devlink_port->type_lock); |
5682 | devlink_port->type = type; | 6283 | devlink_port->type = type; |
5683 | devlink_port->type_dev = type_dev; | 6284 | devlink_port->type_dev = type_dev; |
5684 | spin_unlock(&devlink_port->type_lock); | 6285 | spin_unlock_bh(&devlink_port->type_lock); |
5685 | devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); | 6286 | devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); |
5686 | } | 6287 | } |
5687 | 6288 | ||
@@ -6834,6 +7435,475 @@ unlock: | |||
6834 | } | 7435 | } |
6835 | EXPORT_SYMBOL_GPL(devlink_region_snapshot_create); | 7436 | EXPORT_SYMBOL_GPL(devlink_region_snapshot_create); |
6836 | 7437 | ||
7438 | #define DEVLINK_TRAP(_id, _type) \ | ||
7439 | { \ | ||
7440 | .type = DEVLINK_TRAP_TYPE_##_type, \ | ||
7441 | .id = DEVLINK_TRAP_GENERIC_ID_##_id, \ | ||
7442 | .name = DEVLINK_TRAP_GENERIC_NAME_##_id, \ | ||
7443 | } | ||
7444 | |||
7445 | static const struct devlink_trap devlink_trap_generic[] = { | ||
7446 | DEVLINK_TRAP(SMAC_MC, DROP), | ||
7447 | DEVLINK_TRAP(VLAN_TAG_MISMATCH, DROP), | ||
7448 | DEVLINK_TRAP(INGRESS_VLAN_FILTER, DROP), | ||
7449 | DEVLINK_TRAP(INGRESS_STP_FILTER, DROP), | ||
7450 | DEVLINK_TRAP(EMPTY_TX_LIST, DROP), | ||
7451 | DEVLINK_TRAP(PORT_LOOPBACK_FILTER, DROP), | ||
7452 | DEVLINK_TRAP(BLACKHOLE_ROUTE, DROP), | ||
7453 | DEVLINK_TRAP(TTL_ERROR, EXCEPTION), | ||
7454 | DEVLINK_TRAP(TAIL_DROP, DROP), | ||
7455 | }; | ||
7456 | |||
7457 | #define DEVLINK_TRAP_GROUP(_id) \ | ||
7458 | { \ | ||
7459 | .id = DEVLINK_TRAP_GROUP_GENERIC_ID_##_id, \ | ||
7460 | .name = DEVLINK_TRAP_GROUP_GENERIC_NAME_##_id, \ | ||
7461 | } | ||
7462 | |||
7463 | static const struct devlink_trap_group devlink_trap_group_generic[] = { | ||
7464 | DEVLINK_TRAP_GROUP(L2_DROPS), | ||
7465 | DEVLINK_TRAP_GROUP(L3_DROPS), | ||
7466 | DEVLINK_TRAP_GROUP(BUFFER_DROPS), | ||
7467 | }; | ||
7468 | |||
7469 | static int devlink_trap_generic_verify(const struct devlink_trap *trap) | ||
7470 | { | ||
7471 | if (trap->id > DEVLINK_TRAP_GENERIC_ID_MAX) | ||
7472 | return -EINVAL; | ||
7473 | |||
7474 | if (strcmp(trap->name, devlink_trap_generic[trap->id].name)) | ||
7475 | return -EINVAL; | ||
7476 | |||
7477 | if (trap->type != devlink_trap_generic[trap->id].type) | ||
7478 | return -EINVAL; | ||
7479 | |||
7480 | return 0; | ||
7481 | } | ||
7482 | |||
7483 | static int devlink_trap_driver_verify(const struct devlink_trap *trap) | ||
7484 | { | ||
7485 | int i; | ||
7486 | |||
7487 | if (trap->id <= DEVLINK_TRAP_GENERIC_ID_MAX) | ||
7488 | return -EINVAL; | ||
7489 | |||
7490 | for (i = 0; i < ARRAY_SIZE(devlink_trap_generic); i++) { | ||
7491 | if (!strcmp(trap->name, devlink_trap_generic[i].name)) | ||
7492 | return -EEXIST; | ||
7493 | } | ||
7494 | |||
7495 | return 0; | ||
7496 | } | ||
7497 | |||
7498 | static int devlink_trap_verify(const struct devlink_trap *trap) | ||
7499 | { | ||
7500 | if (!trap || !trap->name || !trap->group.name) | ||
7501 | return -EINVAL; | ||
7502 | |||
7503 | if (trap->generic) | ||
7504 | return devlink_trap_generic_verify(trap); | ||
7505 | else | ||
7506 | return devlink_trap_driver_verify(trap); | ||
7507 | } | ||
7508 | |||
7509 | static int | ||
7510 | devlink_trap_group_generic_verify(const struct devlink_trap_group *group) | ||
7511 | { | ||
7512 | if (group->id > DEVLINK_TRAP_GROUP_GENERIC_ID_MAX) | ||
7513 | return -EINVAL; | ||
7514 | |||
7515 | if (strcmp(group->name, devlink_trap_group_generic[group->id].name)) | ||
7516 | return -EINVAL; | ||
7517 | |||
7518 | return 0; | ||
7519 | } | ||
7520 | |||
7521 | static int | ||
7522 | devlink_trap_group_driver_verify(const struct devlink_trap_group *group) | ||
7523 | { | ||
7524 | int i; | ||
7525 | |||
7526 | if (group->id <= DEVLINK_TRAP_GROUP_GENERIC_ID_MAX) | ||
7527 | return -EINVAL; | ||
7528 | |||
7529 | for (i = 0; i < ARRAY_SIZE(devlink_trap_group_generic); i++) { | ||
7530 | if (!strcmp(group->name, devlink_trap_group_generic[i].name)) | ||
7531 | return -EEXIST; | ||
7532 | } | ||
7533 | |||
7534 | return 0; | ||
7535 | } | ||
7536 | |||
7537 | static int devlink_trap_group_verify(const struct devlink_trap_group *group) | ||
7538 | { | ||
7539 | if (group->generic) | ||
7540 | return devlink_trap_group_generic_verify(group); | ||
7541 | else | ||
7542 | return devlink_trap_group_driver_verify(group); | ||
7543 | } | ||
7544 | |||
7545 | static void | ||
7546 | devlink_trap_group_notify(struct devlink *devlink, | ||
7547 | const struct devlink_trap_group_item *group_item, | ||
7548 | enum devlink_command cmd) | ||
7549 | { | ||
7550 | struct sk_buff *msg; | ||
7551 | int err; | ||
7552 | |||
7553 | WARN_ON_ONCE(cmd != DEVLINK_CMD_TRAP_GROUP_NEW && | ||
7554 | cmd != DEVLINK_CMD_TRAP_GROUP_DEL); | ||
7555 | |||
7556 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
7557 | if (!msg) | ||
7558 | return; | ||
7559 | |||
7560 | err = devlink_nl_trap_group_fill(msg, devlink, group_item, cmd, 0, 0, | ||
7561 | 0); | ||
7562 | if (err) { | ||
7563 | nlmsg_free(msg); | ||
7564 | return; | ||
7565 | } | ||
7566 | |||
7567 | genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), | ||
7568 | msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); | ||
7569 | } | ||
7570 | |||
7571 | static struct devlink_trap_group_item * | ||
7572 | devlink_trap_group_item_create(struct devlink *devlink, | ||
7573 | const struct devlink_trap_group *group) | ||
7574 | { | ||
7575 | struct devlink_trap_group_item *group_item; | ||
7576 | int err; | ||
7577 | |||
7578 | err = devlink_trap_group_verify(group); | ||
7579 | if (err) | ||
7580 | return ERR_PTR(err); | ||
7581 | |||
7582 | group_item = kzalloc(sizeof(*group_item), GFP_KERNEL); | ||
7583 | if (!group_item) | ||
7584 | return ERR_PTR(-ENOMEM); | ||
7585 | |||
7586 | group_item->stats = netdev_alloc_pcpu_stats(struct devlink_stats); | ||
7587 | if (!group_item->stats) { | ||
7588 | err = -ENOMEM; | ||
7589 | goto err_stats_alloc; | ||
7590 | } | ||
7591 | |||
7592 | group_item->group = group; | ||
7593 | refcount_set(&group_item->refcount, 1); | ||
7594 | |||
7595 | if (devlink->ops->trap_group_init) { | ||
7596 | err = devlink->ops->trap_group_init(devlink, group); | ||
7597 | if (err) | ||
7598 | goto err_group_init; | ||
7599 | } | ||
7600 | |||
7601 | list_add_tail(&group_item->list, &devlink->trap_group_list); | ||
7602 | devlink_trap_group_notify(devlink, group_item, | ||
7603 | DEVLINK_CMD_TRAP_GROUP_NEW); | ||
7604 | |||
7605 | return group_item; | ||
7606 | |||
7607 | err_group_init: | ||
7608 | free_percpu(group_item->stats); | ||
7609 | err_stats_alloc: | ||
7610 | kfree(group_item); | ||
7611 | return ERR_PTR(err); | ||
7612 | } | ||
7613 | |||
7614 | static void | ||
7615 | devlink_trap_group_item_destroy(struct devlink *devlink, | ||
7616 | struct devlink_trap_group_item *group_item) | ||
7617 | { | ||
7618 | devlink_trap_group_notify(devlink, group_item, | ||
7619 | DEVLINK_CMD_TRAP_GROUP_DEL); | ||
7620 | list_del(&group_item->list); | ||
7621 | free_percpu(group_item->stats); | ||
7622 | kfree(group_item); | ||
7623 | } | ||
7624 | |||
7625 | static struct devlink_trap_group_item * | ||
7626 | devlink_trap_group_item_get(struct devlink *devlink, | ||
7627 | const struct devlink_trap_group *group) | ||
7628 | { | ||
7629 | struct devlink_trap_group_item *group_item; | ||
7630 | |||
7631 | group_item = devlink_trap_group_item_lookup(devlink, group->name); | ||
7632 | if (group_item) { | ||
7633 | refcount_inc(&group_item->refcount); | ||
7634 | return group_item; | ||
7635 | } | ||
7636 | |||
7637 | return devlink_trap_group_item_create(devlink, group); | ||
7638 | } | ||
7639 | |||
7640 | static void | ||
7641 | devlink_trap_group_item_put(struct devlink *devlink, | ||
7642 | struct devlink_trap_group_item *group_item) | ||
7643 | { | ||
7644 | if (!refcount_dec_and_test(&group_item->refcount)) | ||
7645 | return; | ||
7646 | |||
7647 | devlink_trap_group_item_destroy(devlink, group_item); | ||
7648 | } | ||
7649 | |||
7650 | static int | ||
7651 | devlink_trap_item_group_link(struct devlink *devlink, | ||
7652 | struct devlink_trap_item *trap_item) | ||
7653 | { | ||
7654 | struct devlink_trap_group_item *group_item; | ||
7655 | |||
7656 | group_item = devlink_trap_group_item_get(devlink, | ||
7657 | &trap_item->trap->group); | ||
7658 | if (IS_ERR(group_item)) | ||
7659 | return PTR_ERR(group_item); | ||
7660 | |||
7661 | trap_item->group_item = group_item; | ||
7662 | |||
7663 | return 0; | ||
7664 | } | ||
7665 | |||
7666 | static void | ||
7667 | devlink_trap_item_group_unlink(struct devlink *devlink, | ||
7668 | struct devlink_trap_item *trap_item) | ||
7669 | { | ||
7670 | devlink_trap_group_item_put(devlink, trap_item->group_item); | ||
7671 | } | ||
7672 | |||
7673 | static void devlink_trap_notify(struct devlink *devlink, | ||
7674 | const struct devlink_trap_item *trap_item, | ||
7675 | enum devlink_command cmd) | ||
7676 | { | ||
7677 | struct sk_buff *msg; | ||
7678 | int err; | ||
7679 | |||
7680 | WARN_ON_ONCE(cmd != DEVLINK_CMD_TRAP_NEW && | ||
7681 | cmd != DEVLINK_CMD_TRAP_DEL); | ||
7682 | |||
7683 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
7684 | if (!msg) | ||
7685 | return; | ||
7686 | |||
7687 | err = devlink_nl_trap_fill(msg, devlink, trap_item, cmd, 0, 0, 0); | ||
7688 | if (err) { | ||
7689 | nlmsg_free(msg); | ||
7690 | return; | ||
7691 | } | ||
7692 | |||
7693 | genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), | ||
7694 | msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); | ||
7695 | } | ||
7696 | |||
7697 | static int | ||
7698 | devlink_trap_register(struct devlink *devlink, | ||
7699 | const struct devlink_trap *trap, void *priv) | ||
7700 | { | ||
7701 | struct devlink_trap_item *trap_item; | ||
7702 | int err; | ||
7703 | |||
7704 | if (devlink_trap_item_lookup(devlink, trap->name)) | ||
7705 | return -EEXIST; | ||
7706 | |||
7707 | trap_item = kzalloc(sizeof(*trap_item), GFP_KERNEL); | ||
7708 | if (!trap_item) | ||
7709 | return -ENOMEM; | ||
7710 | |||
7711 | trap_item->stats = netdev_alloc_pcpu_stats(struct devlink_stats); | ||
7712 | if (!trap_item->stats) { | ||
7713 | err = -ENOMEM; | ||
7714 | goto err_stats_alloc; | ||
7715 | } | ||
7716 | |||
7717 | trap_item->trap = trap; | ||
7718 | trap_item->action = trap->init_action; | ||
7719 | trap_item->priv = priv; | ||
7720 | |||
7721 | err = devlink_trap_item_group_link(devlink, trap_item); | ||
7722 | if (err) | ||
7723 | goto err_group_link; | ||
7724 | |||
7725 | err = devlink->ops->trap_init(devlink, trap, trap_item); | ||
7726 | if (err) | ||
7727 | goto err_trap_init; | ||
7728 | |||
7729 | list_add_tail(&trap_item->list, &devlink->trap_list); | ||
7730 | devlink_trap_notify(devlink, trap_item, DEVLINK_CMD_TRAP_NEW); | ||
7731 | |||
7732 | return 0; | ||
7733 | |||
7734 | err_trap_init: | ||
7735 | devlink_trap_item_group_unlink(devlink, trap_item); | ||
7736 | err_group_link: | ||
7737 | free_percpu(trap_item->stats); | ||
7738 | err_stats_alloc: | ||
7739 | kfree(trap_item); | ||
7740 | return err; | ||
7741 | } | ||
7742 | |||
7743 | static void devlink_trap_unregister(struct devlink *devlink, | ||
7744 | const struct devlink_trap *trap) | ||
7745 | { | ||
7746 | struct devlink_trap_item *trap_item; | ||
7747 | |||
7748 | trap_item = devlink_trap_item_lookup(devlink, trap->name); | ||
7749 | if (WARN_ON_ONCE(!trap_item)) | ||
7750 | return; | ||
7751 | |||
7752 | devlink_trap_notify(devlink, trap_item, DEVLINK_CMD_TRAP_DEL); | ||
7753 | list_del(&trap_item->list); | ||
7754 | if (devlink->ops->trap_fini) | ||
7755 | devlink->ops->trap_fini(devlink, trap, trap_item); | ||
7756 | devlink_trap_item_group_unlink(devlink, trap_item); | ||
7757 | free_percpu(trap_item->stats); | ||
7758 | kfree(trap_item); | ||
7759 | } | ||
7760 | |||
7761 | static void devlink_trap_disable(struct devlink *devlink, | ||
7762 | const struct devlink_trap *trap) | ||
7763 | { | ||
7764 | struct devlink_trap_item *trap_item; | ||
7765 | |||
7766 | trap_item = devlink_trap_item_lookup(devlink, trap->name); | ||
7767 | if (WARN_ON_ONCE(!trap_item)) | ||
7768 | return; | ||
7769 | |||
7770 | devlink->ops->trap_action_set(devlink, trap, DEVLINK_TRAP_ACTION_DROP); | ||
7771 | trap_item->action = DEVLINK_TRAP_ACTION_DROP; | ||
7772 | } | ||
7773 | |||
7774 | /** | ||
7775 | * devlink_traps_register - Register packet traps with devlink. | ||
7776 | * @devlink: devlink. | ||
7777 | * @traps: Packet traps. | ||
7778 | * @traps_count: Count of provided packet traps. | ||
7779 | * @priv: Driver private information. | ||
7780 | * | ||
7781 | * Return: Non-zero value on failure. | ||
7782 | */ | ||
7783 | int devlink_traps_register(struct devlink *devlink, | ||
7784 | const struct devlink_trap *traps, | ||
7785 | size_t traps_count, void *priv) | ||
7786 | { | ||
7787 | int i, err; | ||
7788 | |||
7789 | if (!devlink->ops->trap_init || !devlink->ops->trap_action_set) | ||
7790 | return -EINVAL; | ||
7791 | |||
7792 | mutex_lock(&devlink->lock); | ||
7793 | for (i = 0; i < traps_count; i++) { | ||
7794 | const struct devlink_trap *trap = &traps[i]; | ||
7795 | |||
7796 | err = devlink_trap_verify(trap); | ||
7797 | if (err) | ||
7798 | goto err_trap_verify; | ||
7799 | |||
7800 | err = devlink_trap_register(devlink, trap, priv); | ||
7801 | if (err) | ||
7802 | goto err_trap_register; | ||
7803 | } | ||
7804 | mutex_unlock(&devlink->lock); | ||
7805 | |||
7806 | return 0; | ||
7807 | |||
7808 | err_trap_register: | ||
7809 | err_trap_verify: | ||
7810 | for (i--; i >= 0; i--) | ||
7811 | devlink_trap_unregister(devlink, &traps[i]); | ||
7812 | mutex_unlock(&devlink->lock); | ||
7813 | return err; | ||
7814 | } | ||
7815 | EXPORT_SYMBOL_GPL(devlink_traps_register); | ||
7816 | |||
7817 | /** | ||
7818 | * devlink_traps_unregister - Unregister packet traps from devlink. | ||
7819 | * @devlink: devlink. | ||
7820 | * @traps: Packet traps. | ||
7821 | * @traps_count: Count of provided packet traps. | ||
7822 | */ | ||
7823 | void devlink_traps_unregister(struct devlink *devlink, | ||
7824 | const struct devlink_trap *traps, | ||
7825 | size_t traps_count) | ||
7826 | { | ||
7827 | int i; | ||
7828 | |||
7829 | mutex_lock(&devlink->lock); | ||
7830 | /* Make sure we do not have any packets in-flight while unregistering | ||
7831 | * traps by disabling all of them and waiting for a grace period. | ||
7832 | */ | ||
7833 | for (i = traps_count - 1; i >= 0; i--) | ||
7834 | devlink_trap_disable(devlink, &traps[i]); | ||
7835 | synchronize_rcu(); | ||
7836 | for (i = traps_count - 1; i >= 0; i--) | ||
7837 | devlink_trap_unregister(devlink, &traps[i]); | ||
7838 | mutex_unlock(&devlink->lock); | ||
7839 | } | ||
7840 | EXPORT_SYMBOL_GPL(devlink_traps_unregister); | ||
7841 | |||
7842 | static void | ||
7843 | devlink_trap_stats_update(struct devlink_stats __percpu *trap_stats, | ||
7844 | size_t skb_len) | ||
7845 | { | ||
7846 | struct devlink_stats *stats; | ||
7847 | |||
7848 | stats = this_cpu_ptr(trap_stats); | ||
7849 | u64_stats_update_begin(&stats->syncp); | ||
7850 | stats->rx_bytes += skb_len; | ||
7851 | stats->rx_packets++; | ||
7852 | u64_stats_update_end(&stats->syncp); | ||
7853 | } | ||
7854 | |||
7855 | static void | ||
7856 | devlink_trap_report_metadata_fill(struct net_dm_hw_metadata *hw_metadata, | ||
7857 | const struct devlink_trap_item *trap_item, | ||
7858 | struct devlink_port *in_devlink_port) | ||
7859 | { | ||
7860 | struct devlink_trap_group_item *group_item = trap_item->group_item; | ||
7861 | |||
7862 | hw_metadata->trap_group_name = group_item->group->name; | ||
7863 | hw_metadata->trap_name = trap_item->trap->name; | ||
7864 | |||
7865 | spin_lock(&in_devlink_port->type_lock); | ||
7866 | if (in_devlink_port->type == DEVLINK_PORT_TYPE_ETH) | ||
7867 | hw_metadata->input_dev = in_devlink_port->type_dev; | ||
7868 | spin_unlock(&in_devlink_port->type_lock); | ||
7869 | } | ||
7870 | |||
7871 | /** | ||
7872 | * devlink_trap_report - Report trapped packet to drop monitor. | ||
7873 | * @devlink: devlink. | ||
7874 | * @skb: Trapped packet. | ||
7875 | * @trap_ctx: Trap context. | ||
7876 | * @in_devlink_port: Input devlink port. | ||
7877 | */ | ||
7878 | void devlink_trap_report(struct devlink *devlink, struct sk_buff *skb, | ||
7879 | void *trap_ctx, struct devlink_port *in_devlink_port) | ||
7880 | { | ||
7881 | struct devlink_trap_item *trap_item = trap_ctx; | ||
7882 | struct net_dm_hw_metadata hw_metadata = {}; | ||
7883 | |||
7884 | devlink_trap_stats_update(trap_item->stats, skb->len); | ||
7885 | devlink_trap_stats_update(trap_item->group_item->stats, skb->len); | ||
7886 | |||
7887 | devlink_trap_report_metadata_fill(&hw_metadata, trap_item, | ||
7888 | in_devlink_port); | ||
7889 | net_dm_hw_report(skb, &hw_metadata); | ||
7890 | } | ||
7891 | EXPORT_SYMBOL_GPL(devlink_trap_report); | ||
7892 | |||
7893 | /** | ||
7894 | * devlink_trap_ctx_priv - Trap context to driver private information. | ||
7895 | * @trap_ctx: Trap context. | ||
7896 | * | ||
7897 | * Return: Driver private information passed during registration. | ||
7898 | */ | ||
7899 | void *devlink_trap_ctx_priv(void *trap_ctx) | ||
7900 | { | ||
7901 | struct devlink_trap_item *trap_item = trap_ctx; | ||
7902 | |||
7903 | return trap_item->priv; | ||
7904 | } | ||
7905 | EXPORT_SYMBOL_GPL(devlink_trap_ctx_priv); | ||
7906 | |||
6837 | static void __devlink_compat_running_version(struct devlink *devlink, | 7907 | static void __devlink_compat_running_version(struct devlink *devlink, |
6838 | char *buf, size_t len) | 7908 | char *buf, size_t len) |
6839 | { | 7909 | { |