aboutsummaryrefslogtreecommitdiffstats
path: root/net/switchdev/switchdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r--net/switchdev/switchdev.c193
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
356static int __switchdev_port_obj_add(struct net_device *dev, 356static 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
382static int switchdev_port_obj_add_now(struct net_device *dev, 381static 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);
471static int switchdev_port_obj_del_now(struct net_device *dev, 472static 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
496static void switchdev_port_obj_del_deferred(struct net_device *dev, 479static void switchdev_port_obj_del_deferred(struct net_device *dev,
@@ -535,6 +518,7 @@ int switchdev_port_obj_del(struct net_device *dev,
535EXPORT_SYMBOL_GPL(switchdev_port_obj_del); 518EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
536 519
537static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); 520static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
521static 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}
577EXPORT_SYMBOL_GPL(call_switchdev_notifiers); 561EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
578 562
563int 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}
569EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
570
571int 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}
577EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
578
579int 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}
586EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
587
579bool switchdev_port_same_parent_id(struct net_device *a, 588bool 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}
597EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id); 606EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);
607
608static 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
642int 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}
657EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
658
659static 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
692int 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}
706EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);