diff options
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r-- | net/switchdev/switchdev.c | 193 |
1 files changed, 151 insertions, 42 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 74b9d916a58b..fe23fac4dc4b 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c | |||
@@ -353,30 +353,29 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj) | |||
353 | return 0; | 353 | return 0; |
354 | } | 354 | } |
355 | 355 | ||
356 | static int __switchdev_port_obj_add(struct net_device *dev, | 356 | static int switchdev_port_obj_notify(enum switchdev_notifier_type nt, |
357 | const struct switchdev_obj *obj, | 357 | struct net_device *dev, |
358 | struct switchdev_trans *trans) | 358 | const struct switchdev_obj *obj, |
359 | struct switchdev_trans *trans) | ||
359 | { | 360 | { |
360 | const struct switchdev_ops *ops = dev->switchdev_ops; | 361 | int rc; |
361 | struct net_device *lower_dev; | 362 | int err; |
362 | struct list_head *iter; | ||
363 | int err = -EOPNOTSUPP; | ||
364 | |||
365 | if (ops && ops->switchdev_port_obj_add) | ||
366 | return ops->switchdev_port_obj_add(dev, obj, trans); | ||
367 | 363 | ||
368 | /* Switch device port(s) may be stacked under | 364 | struct switchdev_notifier_port_obj_info obj_info = { |
369 | * bond/team/vlan dev, so recurse down to add object on | 365 | .obj = obj, |
370 | * each port. | 366 | .trans = trans, |
371 | */ | 367 | .handled = false, |
368 | }; | ||
372 | 369 | ||
373 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | 370 | rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info); |
374 | err = __switchdev_port_obj_add(lower_dev, obj, trans); | 371 | err = notifier_to_errno(rc); |
375 | if (err) | 372 | if (err) { |
376 | break; | 373 | WARN_ON(!obj_info.handled); |
374 | return err; | ||
377 | } | 375 | } |
378 | 376 | if (!obj_info.handled) | |
379 | return err; | 377 | return -EOPNOTSUPP; |
378 | return 0; | ||
380 | } | 379 | } |
381 | 380 | ||
382 | static int switchdev_port_obj_add_now(struct net_device *dev, | 381 | static int switchdev_port_obj_add_now(struct net_device *dev, |
@@ -397,7 +396,8 @@ static int switchdev_port_obj_add_now(struct net_device *dev, | |||
397 | */ | 396 | */ |
398 | 397 | ||
399 | trans.ph_prepare = true; | 398 | trans.ph_prepare = true; |
400 | err = __switchdev_port_obj_add(dev, obj, &trans); | 399 | err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD, |
400 | dev, obj, &trans); | ||
401 | if (err) { | 401 | if (err) { |
402 | /* Prepare phase failed: abort the transaction. Any | 402 | /* Prepare phase failed: abort the transaction. Any |
403 | * resources reserved in the prepare phase are | 403 | * resources reserved in the prepare phase are |
@@ -416,7 +416,8 @@ static int switchdev_port_obj_add_now(struct net_device *dev, | |||
416 | */ | 416 | */ |
417 | 417 | ||
418 | trans.ph_prepare = false; | 418 | trans.ph_prepare = false; |
419 | err = __switchdev_port_obj_add(dev, obj, &trans); | 419 | err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD, |
420 | dev, obj, &trans); | ||
420 | WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id); | 421 | WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id); |
421 | switchdev_trans_items_warn_destroy(dev, &trans); | 422 | switchdev_trans_items_warn_destroy(dev, &trans); |
422 | 423 | ||
@@ -471,26 +472,8 @@ EXPORT_SYMBOL_GPL(switchdev_port_obj_add); | |||
471 | static int switchdev_port_obj_del_now(struct net_device *dev, | 472 | static int switchdev_port_obj_del_now(struct net_device *dev, |
472 | const struct switchdev_obj *obj) | 473 | const struct switchdev_obj *obj) |
473 | { | 474 | { |
474 | const struct switchdev_ops *ops = dev->switchdev_ops; | 475 | return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL, |
475 | struct net_device *lower_dev; | 476 | dev, obj, NULL); |
476 | struct list_head *iter; | ||
477 | int err = -EOPNOTSUPP; | ||
478 | |||
479 | if (ops && ops->switchdev_port_obj_del) | ||
480 | return ops->switchdev_port_obj_del(dev, obj); | ||
481 | |||
482 | /* Switch device port(s) may be stacked under | ||
483 | * bond/team/vlan dev, so recurse down to delete object on | ||
484 | * each port. | ||
485 | */ | ||
486 | |||
487 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
488 | err = switchdev_port_obj_del_now(lower_dev, obj); | ||
489 | if (err) | ||
490 | break; | ||
491 | } | ||
492 | |||
493 | return err; | ||
494 | } | 477 | } |
495 | 478 | ||
496 | static void switchdev_port_obj_del_deferred(struct net_device *dev, | 479 | static void switchdev_port_obj_del_deferred(struct net_device *dev, |
@@ -535,6 +518,7 @@ int switchdev_port_obj_del(struct net_device *dev, | |||
535 | EXPORT_SYMBOL_GPL(switchdev_port_obj_del); | 518 | EXPORT_SYMBOL_GPL(switchdev_port_obj_del); |
536 | 519 | ||
537 | static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); | 520 | static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); |
521 | static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain); | ||
538 | 522 | ||
539 | /** | 523 | /** |
540 | * register_switchdev_notifier - Register notifier | 524 | * register_switchdev_notifier - Register notifier |
@@ -576,6 +560,31 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev, | |||
576 | } | 560 | } |
577 | EXPORT_SYMBOL_GPL(call_switchdev_notifiers); | 561 | EXPORT_SYMBOL_GPL(call_switchdev_notifiers); |
578 | 562 | ||
563 | int register_switchdev_blocking_notifier(struct notifier_block *nb) | ||
564 | { | ||
565 | struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain; | ||
566 | |||
567 | return blocking_notifier_chain_register(chain, nb); | ||
568 | } | ||
569 | EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier); | ||
570 | |||
571 | int unregister_switchdev_blocking_notifier(struct notifier_block *nb) | ||
572 | { | ||
573 | struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain; | ||
574 | |||
575 | return blocking_notifier_chain_unregister(chain, nb); | ||
576 | } | ||
577 | EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier); | ||
578 | |||
579 | int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev, | ||
580 | struct switchdev_notifier_info *info) | ||
581 | { | ||
582 | info->dev = dev; | ||
583 | return blocking_notifier_call_chain(&switchdev_blocking_notif_chain, | ||
584 | val, info); | ||
585 | } | ||
586 | EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers); | ||
587 | |||
579 | bool switchdev_port_same_parent_id(struct net_device *a, | 588 | bool switchdev_port_same_parent_id(struct net_device *a, |
580 | struct net_device *b) | 589 | struct net_device *b) |
581 | { | 590 | { |
@@ -595,3 +604,103 @@ bool switchdev_port_same_parent_id(struct net_device *a, | |||
595 | return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid); | 604 | return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid); |
596 | } | 605 | } |
597 | EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id); | 606 | EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id); |
607 | |||
608 | static int __switchdev_handle_port_obj_add(struct net_device *dev, | ||
609 | struct switchdev_notifier_port_obj_info *port_obj_info, | ||
610 | bool (*check_cb)(const struct net_device *dev), | ||
611 | int (*add_cb)(struct net_device *dev, | ||
612 | const struct switchdev_obj *obj, | ||
613 | struct switchdev_trans *trans)) | ||
614 | { | ||
615 | struct net_device *lower_dev; | ||
616 | struct list_head *iter; | ||
617 | int err = -EOPNOTSUPP; | ||
618 | |||
619 | if (check_cb(dev)) { | ||
620 | /* This flag is only checked if the return value is success. */ | ||
621 | port_obj_info->handled = true; | ||
622 | return add_cb(dev, port_obj_info->obj, port_obj_info->trans); | ||
623 | } | ||
624 | |||
625 | /* Switch ports might be stacked under e.g. a LAG. Ignore the | ||
626 | * unsupported devices, another driver might be able to handle them. But | ||
627 | * propagate to the callers any hard errors. | ||
628 | * | ||
629 | * If the driver does its own bookkeeping of stacked ports, it's not | ||
630 | * necessary to go through this helper. | ||
631 | */ | ||
632 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
633 | err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info, | ||
634 | check_cb, add_cb); | ||
635 | if (err && err != -EOPNOTSUPP) | ||
636 | return err; | ||
637 | } | ||
638 | |||
639 | return err; | ||
640 | } | ||
641 | |||
642 | int switchdev_handle_port_obj_add(struct net_device *dev, | ||
643 | struct switchdev_notifier_port_obj_info *port_obj_info, | ||
644 | bool (*check_cb)(const struct net_device *dev), | ||
645 | int (*add_cb)(struct net_device *dev, | ||
646 | const struct switchdev_obj *obj, | ||
647 | struct switchdev_trans *trans)) | ||
648 | { | ||
649 | int err; | ||
650 | |||
651 | err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb, | ||
652 | add_cb); | ||
653 | if (err == -EOPNOTSUPP) | ||
654 | err = 0; | ||
655 | return err; | ||
656 | } | ||
657 | EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add); | ||
658 | |||
659 | static int __switchdev_handle_port_obj_del(struct net_device *dev, | ||
660 | struct switchdev_notifier_port_obj_info *port_obj_info, | ||
661 | bool (*check_cb)(const struct net_device *dev), | ||
662 | int (*del_cb)(struct net_device *dev, | ||
663 | const struct switchdev_obj *obj)) | ||
664 | { | ||
665 | struct net_device *lower_dev; | ||
666 | struct list_head *iter; | ||
667 | int err = -EOPNOTSUPP; | ||
668 | |||
669 | if (check_cb(dev)) { | ||
670 | /* This flag is only checked if the return value is success. */ | ||
671 | port_obj_info->handled = true; | ||
672 | return del_cb(dev, port_obj_info->obj); | ||
673 | } | ||
674 | |||
675 | /* Switch ports might be stacked under e.g. a LAG. Ignore the | ||
676 | * unsupported devices, another driver might be able to handle them. But | ||
677 | * propagate to the callers any hard errors. | ||
678 | * | ||
679 | * If the driver does its own bookkeeping of stacked ports, it's not | ||
680 | * necessary to go through this helper. | ||
681 | */ | ||
682 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
683 | err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info, | ||
684 | check_cb, del_cb); | ||
685 | if (err && err != -EOPNOTSUPP) | ||
686 | return err; | ||
687 | } | ||
688 | |||
689 | return err; | ||
690 | } | ||
691 | |||
692 | int switchdev_handle_port_obj_del(struct net_device *dev, | ||
693 | struct switchdev_notifier_port_obj_info *port_obj_info, | ||
694 | bool (*check_cb)(const struct net_device *dev), | ||
695 | int (*del_cb)(struct net_device *dev, | ||
696 | const struct switchdev_obj *obj)) | ||
697 | { | ||
698 | int err; | ||
699 | |||
700 | err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb, | ||
701 | del_cb); | ||
702 | if (err == -EOPNOTSUPP) | ||
703 | err = 0; | ||
704 | return err; | ||
705 | } | ||
706 | EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del); | ||