aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-11-23 21:02:24 -0500
committerDavid S. Miller <davem@davemloft.net>2018-11-23 21:02:24 -0500
commit06d212900ea9183e707218c04f4e77fc204a4fde (patch)
treecb89dc52126ff9665c92f32cd61613316c607093
parent2eb487c1cf3d93b3863c1d0987d7a0e1fbd98a4d (diff)
parentab4a16869f25648570976a4d55923d0c9e7effb8 (diff)
Merge branch 'switchdev-blocking-notifiers'
Petr Machata says: ==================== switchdev: Convert switchdev_port_obj_{add,del}() to notifiers An offloading driver may need to have access to switchdev events on ports that aren't directly under its control. An example is a VXLAN port attached to a bridge offloaded by a driver. The driver needs to know about VLANs configured on the VXLAN device. However the VXLAN device isn't stashed between the bridge and a front-panel-port device (such as is the case e.g. for LAG devices), so the usual switchdev ops don't reach the driver. VXLAN is likely not the only device type like this: in theory any L2 tunnel device that needs offloading will prompt requirement of this sort. A way to fix this is to give up the notion of port object addition / deletion as a switchdev operation, which assumes somewhat tight coupling between the message producer and consumer. And instead send the message over a notifier chain. The series starts with a clean-up patch #1, where SWITCHDEV_OBJ_PORT_{VLAN, MDB}() are fixed up to lift the constraint that the passed-in argument be a simple variable named "obj". switchdev_port_obj_add and _del are invoked in a context that permits blocking. Not only that, at least for the VLAN notification, being able to signal failure is actually important. Therefore introduce a new blocking notifier chain that the new events will be sent on. That's done in patch #2. Retain the current (atomic) notifier chain for the preexisting notifications. In patch #3, introduce two new switchdev notifier types, SWITCHDEV_PORT_OBJ_ADD and SWITCHDEV_PORT_OBJ_DEL. These notifier types communicate the same event as the corresponding switchdev op, except in a form of a notification. struct switchdev_notifier_port_obj_info was added to carry the fields that correspond to the switchdev op arguments. An additional field, handled, will be used to communicate back to switchdev that the event has reached an interested party, which will be important for the two-phase commit. In patches #4, #5, and #7, rocker, DSA resp. ethsw are updated to subscribe to the switchdev blocking notifier chain, and handle the new notifier types. #6 introduces a helper to determine whether a netdevice corresponds to a front panel port. What these three drivers have in common is that their ports don't support any uppers besides bridge. That makes it possible to ignore any notifiers that don't reference a front-panel port device, because they are certainly out of scope. Unlike the previous three, mlxsw and ocelot drivers admit stacked devices as uppers. While the current switchdev code recursively descends through layers of lower devices, eventually calling the op on a front-panel port device, the notifier would reference a stacking device that's one of front-panel ports uppers. The filtering is thus more complex. For ocelot, such iteration is currently pretty much required, because there's no bookkeeping of LAG devices. mlxsw does keep the list of LAGs, however it iterates the lower devices anyway when deciding whether an event on a tunnel device pertains to the driver or not. Therefore this patch set instead introduces, in patch #8, a helper to iterate through lowers, much like the current switchdev code does, looking for devices that match a given predicate. Then in patches #9 and #10, first mlxsw and then ocelot are updated to dispatch the newly-added notifier types to the preexisting port_obj_add/_del handlers. The dispatch is done via the new helper, to recursively descend through lower devices. Finally in patch #11, the actual switch is made, retiring the current SDO-based code in favor of a notifier. Now that the event is distributed through a notifier, the explicit netdevice check in rocker, DSA and ethsw doesn't let through any events except those done on a front-panel port itself. It is therefore unnecessary to check in VLAN-handling code whether a VLAN was added to the bridge itself: such events will simply be ignored much sooner. Therefore remove it in patch #12. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c47
-rw-r--r--drivers/net/ethernet/mscc/ocelot.c30
-rw-r--r--drivers/net/ethernet/mscc/ocelot.h1
-rw-r--r--drivers/net/ethernet/mscc/ocelot_board.c3
-rw-r--r--drivers/net/ethernet/rocker/rocker_main.c60
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/ethsw.c68
-rw-r--r--include/net/switchdev.h87
-rw-r--r--net/dsa/port.c3
-rw-r--r--net/dsa/slave.c58
-rw-r--r--net/switchdev/switchdev.c193
10 files changed, 474 insertions, 76 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index b32a5ee57fb9..73e5db176d7e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -1968,8 +1968,6 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
1968static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = { 1968static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
1969 .switchdev_port_attr_get = mlxsw_sp_port_attr_get, 1969 .switchdev_port_attr_get = mlxsw_sp_port_attr_get,
1970 .switchdev_port_attr_set = mlxsw_sp_port_attr_set, 1970 .switchdev_port_attr_set = mlxsw_sp_port_attr_set,
1971 .switchdev_port_obj_add = mlxsw_sp_port_obj_add,
1972 .switchdev_port_obj_del = mlxsw_sp_port_obj_del,
1973}; 1971};
1974 1972
1975static int 1973static int
@@ -3118,6 +3116,32 @@ static struct notifier_block mlxsw_sp_switchdev_notifier = {
3118 .notifier_call = mlxsw_sp_switchdev_event, 3116 .notifier_call = mlxsw_sp_switchdev_event,
3119}; 3117};
3120 3118
3119static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
3120 unsigned long event, void *ptr)
3121{
3122 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
3123 int err;
3124
3125 switch (event) {
3126 case SWITCHDEV_PORT_OBJ_ADD:
3127 err = switchdev_handle_port_obj_add(dev, ptr,
3128 mlxsw_sp_port_dev_check,
3129 mlxsw_sp_port_obj_add);
3130 return notifier_from_errno(err);
3131 case SWITCHDEV_PORT_OBJ_DEL:
3132 err = switchdev_handle_port_obj_del(dev, ptr,
3133 mlxsw_sp_port_dev_check,
3134 mlxsw_sp_port_obj_del);
3135 return notifier_from_errno(err);
3136 }
3137
3138 return NOTIFY_DONE;
3139}
3140
3141static struct notifier_block mlxsw_sp_switchdev_blocking_notifier = {
3142 .notifier_call = mlxsw_sp_switchdev_blocking_event,
3143};
3144
3121u8 3145u8
3122mlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port) 3146mlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
3123{ 3147{
@@ -3127,6 +3151,7 @@ mlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
3127static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) 3151static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
3128{ 3152{
3129 struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; 3153 struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
3154 struct notifier_block *nb;
3130 int err; 3155 int err;
3131 3156
3132 err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME); 3157 err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME);
@@ -3141,17 +3166,33 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
3141 return err; 3166 return err;
3142 } 3167 }
3143 3168
3169 nb = &mlxsw_sp_switchdev_blocking_notifier;
3170 err = register_switchdev_blocking_notifier(nb);
3171 if (err) {
3172 dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n");
3173 goto err_register_switchdev_blocking_notifier;
3174 }
3175
3144 INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work); 3176 INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
3145 bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; 3177 bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
3146 mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); 3178 mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
3147 return 0; 3179 return 0;
3180
3181err_register_switchdev_blocking_notifier:
3182 unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
3183 return err;
3148} 3184}
3149 3185
3150static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) 3186static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
3151{ 3187{
3188 struct notifier_block *nb;
3189
3152 cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw); 3190 cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw);
3153 unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
3154 3191
3192 nb = &mlxsw_sp_switchdev_blocking_notifier;
3193 unregister_switchdev_blocking_notifier(nb);
3194
3195 unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
3155} 3196}
3156 3197
3157int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) 3198int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 3238b9ee42f3..7f8da8873a96 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -1337,8 +1337,6 @@ static int ocelot_port_obj_del(struct net_device *dev,
1337static const struct switchdev_ops ocelot_port_switchdev_ops = { 1337static const struct switchdev_ops ocelot_port_switchdev_ops = {
1338 .switchdev_port_attr_get = ocelot_port_attr_get, 1338 .switchdev_port_attr_get = ocelot_port_attr_get,
1339 .switchdev_port_attr_set = ocelot_port_attr_set, 1339 .switchdev_port_attr_set = ocelot_port_attr_set,
1340 .switchdev_port_obj_add = ocelot_port_obj_add,
1341 .switchdev_port_obj_del = ocelot_port_obj_del,
1342}; 1340};
1343 1341
1344static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port, 1342static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port,
@@ -1595,6 +1593,34 @@ struct notifier_block ocelot_netdevice_nb __read_mostly = {
1595}; 1593};
1596EXPORT_SYMBOL(ocelot_netdevice_nb); 1594EXPORT_SYMBOL(ocelot_netdevice_nb);
1597 1595
1596static int ocelot_switchdev_blocking_event(struct notifier_block *unused,
1597 unsigned long event, void *ptr)
1598{
1599 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
1600 int err;
1601
1602 switch (event) {
1603 /* Blocking events. */
1604 case SWITCHDEV_PORT_OBJ_ADD:
1605 err = switchdev_handle_port_obj_add(dev, ptr,
1606 ocelot_netdevice_dev_check,
1607 ocelot_port_obj_add);
1608 return notifier_from_errno(err);
1609 case SWITCHDEV_PORT_OBJ_DEL:
1610 err = switchdev_handle_port_obj_del(dev, ptr,
1611 ocelot_netdevice_dev_check,
1612 ocelot_port_obj_del);
1613 return notifier_from_errno(err);
1614 }
1615
1616 return NOTIFY_DONE;
1617}
1618
1619struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = {
1620 .notifier_call = ocelot_switchdev_blocking_event,
1621};
1622EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
1623
1598int ocelot_probe_port(struct ocelot *ocelot, u8 port, 1624int ocelot_probe_port(struct ocelot *ocelot, u8 port,
1599 void __iomem *regs, 1625 void __iomem *regs,
1600 struct phy_device *phy) 1626 struct phy_device *phy)
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index 62c7c8eb00d9..086775f7b52f 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -499,5 +499,6 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
499 struct phy_device *phy); 499 struct phy_device *phy);
500 500
501extern struct notifier_block ocelot_netdevice_nb; 501extern struct notifier_block ocelot_netdevice_nb;
502extern struct notifier_block ocelot_switchdev_blocking_nb;
502 503
503#endif 504#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c
index 4c23d18bbf44..ca3ea2fbfcd0 100644
--- a/drivers/net/ethernet/mscc/ocelot_board.c
+++ b/drivers/net/ethernet/mscc/ocelot_board.c
@@ -12,6 +12,7 @@
12#include <linux/of_platform.h> 12#include <linux/of_platform.h>
13#include <linux/mfd/syscon.h> 13#include <linux/mfd/syscon.h>
14#include <linux/skbuff.h> 14#include <linux/skbuff.h>
15#include <net/switchdev.h>
15 16
16#include "ocelot.h" 17#include "ocelot.h"
17 18
@@ -328,6 +329,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
328 } 329 }
329 330
330 register_netdevice_notifier(&ocelot_netdevice_nb); 331 register_netdevice_notifier(&ocelot_netdevice_nb);
332 register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
331 333
332 dev_info(&pdev->dev, "Ocelot switch probed\n"); 334 dev_info(&pdev->dev, "Ocelot switch probed\n");
333 335
@@ -342,6 +344,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
342 struct ocelot *ocelot = platform_get_drvdata(pdev); 344 struct ocelot *ocelot = platform_get_drvdata(pdev);
343 345
344 ocelot_deinit(ocelot); 346 ocelot_deinit(ocelot);
347 unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
345 unregister_netdevice_notifier(&ocelot_netdevice_nb); 348 unregister_netdevice_notifier(&ocelot_netdevice_nb);
346 349
347 return 0; 350 return 0;
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index beb06628f22d..6213827e3956 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -1632,9 +1632,6 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
1632{ 1632{
1633 struct rocker_world_ops *wops = rocker_port->rocker->wops; 1633 struct rocker_world_ops *wops = rocker_port->rocker->wops;
1634 1634
1635 if (netif_is_bridge_master(vlan->obj.orig_dev))
1636 return -EOPNOTSUPP;
1637
1638 if (!wops->port_obj_vlan_add) 1635 if (!wops->port_obj_vlan_add)
1639 return -EOPNOTSUPP; 1636 return -EOPNOTSUPP;
1640 1637
@@ -2145,8 +2142,6 @@ static int rocker_port_obj_del(struct net_device *dev,
2145static const struct switchdev_ops rocker_port_switchdev_ops = { 2142static const struct switchdev_ops rocker_port_switchdev_ops = {
2146 .switchdev_port_attr_get = rocker_port_attr_get, 2143 .switchdev_port_attr_get = rocker_port_attr_get,
2147 .switchdev_port_attr_set = rocker_port_attr_set, 2144 .switchdev_port_attr_set = rocker_port_attr_set,
2148 .switchdev_port_obj_add = rocker_port_obj_add,
2149 .switchdev_port_obj_del = rocker_port_obj_del,
2150}; 2145};
2151 2146
2152struct rocker_fib_event_work { 2147struct rocker_fib_event_work {
@@ -2812,12 +2807,54 @@ static int rocker_switchdev_event(struct notifier_block *unused,
2812 return NOTIFY_DONE; 2807 return NOTIFY_DONE;
2813} 2808}
2814 2809
2810static int
2811rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
2812 struct switchdev_notifier_port_obj_info *port_obj_info)
2813{
2814 int err = -EOPNOTSUPP;
2815
2816 switch (event) {
2817 case SWITCHDEV_PORT_OBJ_ADD:
2818 err = rocker_port_obj_add(netdev, port_obj_info->obj,
2819 port_obj_info->trans);
2820 break;
2821 case SWITCHDEV_PORT_OBJ_DEL:
2822 err = rocker_port_obj_del(netdev, port_obj_info->obj);
2823 break;
2824 }
2825
2826 port_obj_info->handled = true;
2827 return notifier_from_errno(err);
2828}
2829
2830static int rocker_switchdev_blocking_event(struct notifier_block *unused,
2831 unsigned long event, void *ptr)
2832{
2833 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
2834
2835 if (!rocker_port_dev_check(dev))
2836 return NOTIFY_DONE;
2837
2838 switch (event) {
2839 case SWITCHDEV_PORT_OBJ_ADD:
2840 case SWITCHDEV_PORT_OBJ_DEL:
2841 return rocker_switchdev_port_obj_event(event, dev, ptr);
2842 }
2843
2844 return NOTIFY_DONE;
2845}
2846
2815static struct notifier_block rocker_switchdev_notifier = { 2847static struct notifier_block rocker_switchdev_notifier = {
2816 .notifier_call = rocker_switchdev_event, 2848 .notifier_call = rocker_switchdev_event,
2817}; 2849};
2818 2850
2851static struct notifier_block rocker_switchdev_blocking_notifier = {
2852 .notifier_call = rocker_switchdev_blocking_event,
2853};
2854
2819static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) 2855static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2820{ 2856{
2857 struct notifier_block *nb;
2821 struct rocker *rocker; 2858 struct rocker *rocker;
2822 int err; 2859 int err;
2823 2860
@@ -2933,6 +2970,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2933 goto err_register_switchdev_notifier; 2970 goto err_register_switchdev_notifier;
2934 } 2971 }
2935 2972
2973 nb = &rocker_switchdev_blocking_notifier;
2974 err = register_switchdev_blocking_notifier(nb);
2975 if (err) {
2976 dev_err(&pdev->dev, "Failed to register switchdev blocking notifier\n");
2977 goto err_register_switchdev_blocking_notifier;
2978 }
2979
2936 rocker->hw.id = rocker_read64(rocker, SWITCH_ID); 2980 rocker->hw.id = rocker_read64(rocker, SWITCH_ID);
2937 2981
2938 dev_info(&pdev->dev, "Rocker switch with id %*phN\n", 2982 dev_info(&pdev->dev, "Rocker switch with id %*phN\n",
@@ -2940,6 +2984,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2940 2984
2941 return 0; 2985 return 0;
2942 2986
2987err_register_switchdev_blocking_notifier:
2988 unregister_switchdev_notifier(&rocker_switchdev_notifier);
2943err_register_switchdev_notifier: 2989err_register_switchdev_notifier:
2944 unregister_fib_notifier(&rocker->fib_nb); 2990 unregister_fib_notifier(&rocker->fib_nb);
2945err_register_fib_notifier: 2991err_register_fib_notifier:
@@ -2971,6 +3017,10 @@ err_pci_enable_device:
2971static void rocker_remove(struct pci_dev *pdev) 3017static void rocker_remove(struct pci_dev *pdev)
2972{ 3018{
2973 struct rocker *rocker = pci_get_drvdata(pdev); 3019 struct rocker *rocker = pci_get_drvdata(pdev);
3020 struct notifier_block *nb;
3021
3022 nb = &rocker_switchdev_blocking_notifier;
3023 unregister_switchdev_blocking_notifier(nb);
2974 3024
2975 unregister_switchdev_notifier(&rocker_switchdev_notifier); 3025 unregister_switchdev_notifier(&rocker_switchdev_notifier);
2976 unregister_fib_notifier(&rocker->fib_nb); 3026 unregister_fib_notifier(&rocker->fib_nb);
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
index 7a7ca67822c5..4fa37d6e598b 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
@@ -719,9 +719,6 @@ static int port_vlans_add(struct net_device *netdev,
719 struct ethsw_port_priv *port_priv = netdev_priv(netdev); 719 struct ethsw_port_priv *port_priv = netdev_priv(netdev);
720 int vid, err = 0; 720 int vid, err = 0;
721 721
722 if (netif_is_bridge_master(vlan->obj.orig_dev))
723 return -EOPNOTSUPP;
724
725 if (switchdev_trans_ph_prepare(trans)) 722 if (switchdev_trans_ph_prepare(trans))
726 return 0; 723 return 0;
727 724
@@ -930,8 +927,6 @@ static int swdev_port_obj_del(struct net_device *netdev,
930static const struct switchdev_ops ethsw_port_switchdev_ops = { 927static const struct switchdev_ops ethsw_port_switchdev_ops = {
931 .switchdev_port_attr_get = swdev_port_attr_get, 928 .switchdev_port_attr_get = swdev_port_attr_get,
932 .switchdev_port_attr_set = swdev_port_attr_set, 929 .switchdev_port_attr_set = swdev_port_attr_set,
933 .switchdev_port_obj_add = swdev_port_obj_add,
934 .switchdev_port_obj_del = swdev_port_obj_del,
935}; 930};
936 931
937/* For the moment, only flood setting needs to be updated */ 932/* For the moment, only flood setting needs to be updated */
@@ -972,6 +967,11 @@ static int port_bridge_leave(struct net_device *netdev)
972 return err; 967 return err;
973} 968}
974 969
970static bool ethsw_port_dev_check(const struct net_device *netdev)
971{
972 return netdev->netdev_ops == &ethsw_port_ops;
973}
974
975static int port_netdevice_event(struct notifier_block *unused, 975static int port_netdevice_event(struct notifier_block *unused,
976 unsigned long event, void *ptr) 976 unsigned long event, void *ptr)
977{ 977{
@@ -980,7 +980,7 @@ static int port_netdevice_event(struct notifier_block *unused,
980 struct net_device *upper_dev; 980 struct net_device *upper_dev;
981 int err = 0; 981 int err = 0;
982 982
983 if (netdev->netdev_ops != &ethsw_port_ops) 983 if (!ethsw_port_dev_check(netdev))
984 return NOTIFY_DONE; 984 return NOTIFY_DONE;
985 985
986 /* Handle just upper dev link/unlink for the moment */ 986 /* Handle just upper dev link/unlink for the moment */
@@ -1083,10 +1083,51 @@ err_addr_alloc:
1083 return NOTIFY_BAD; 1083 return NOTIFY_BAD;
1084} 1084}
1085 1085
1086static int
1087ethsw_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
1088 struct switchdev_notifier_port_obj_info *port_obj_info)
1089{
1090 int err = -EOPNOTSUPP;
1091
1092 switch (event) {
1093 case SWITCHDEV_PORT_OBJ_ADD:
1094 err = swdev_port_obj_add(netdev, port_obj_info->obj,
1095 port_obj_info->trans);
1096 break;
1097 case SWITCHDEV_PORT_OBJ_DEL:
1098 err = swdev_port_obj_del(netdev, port_obj_info->obj);
1099 break;
1100 }
1101
1102 port_obj_info->handled = true;
1103 return notifier_from_errno(err);
1104}
1105
1106static int port_switchdev_blocking_event(struct notifier_block *unused,
1107 unsigned long event, void *ptr)
1108{
1109 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
1110
1111 if (!ethsw_port_dev_check(dev))
1112 return NOTIFY_DONE;
1113
1114 switch (event) {
1115 case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
1116 case SWITCHDEV_PORT_OBJ_DEL:
1117 return ethsw_switchdev_port_obj_event(event, dev, ptr);
1118 }
1119
1120 return NOTIFY_DONE;
1121}
1122
1086static struct notifier_block port_switchdev_nb = { 1123static struct notifier_block port_switchdev_nb = {
1087 .notifier_call = port_switchdev_event, 1124 .notifier_call = port_switchdev_event,
1088}; 1125};
1089 1126
1127static struct notifier_block port_switchdev_blocking_nb = {
1128 .notifier_call = port_switchdev_blocking_event,
1129};
1130
1090static int ethsw_register_notifier(struct device *dev) 1131static int ethsw_register_notifier(struct device *dev)
1091{ 1132{
1092 int err; 1133 int err;
@@ -1103,8 +1144,16 @@ static int ethsw_register_notifier(struct device *dev)
1103 goto err_switchdev_nb; 1144 goto err_switchdev_nb;
1104 } 1145 }
1105 1146
1147 err = register_switchdev_blocking_notifier(&port_switchdev_blocking_nb);
1148 if (err) {
1149 dev_err(dev, "Failed to register switchdev blocking notifier\n");
1150 goto err_switchdev_blocking_nb;
1151 }
1152
1106 return 0; 1153 return 0;
1107 1154
1155err_switchdev_blocking_nb:
1156 unregister_switchdev_notifier(&port_switchdev_nb);
1108err_switchdev_nb: 1157err_switchdev_nb:
1109 unregister_netdevice_notifier(&port_nb); 1158 unregister_netdevice_notifier(&port_nb);
1110 return err; 1159 return err;
@@ -1291,8 +1340,15 @@ static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
1291 1340
1292static void ethsw_unregister_notifier(struct device *dev) 1341static void ethsw_unregister_notifier(struct device *dev)
1293{ 1342{
1343 struct notifier_block *nb;
1294 int err; 1344 int err;
1295 1345
1346 nb = &port_switchdev_blocking_nb;
1347 err = unregister_switchdev_blocking_notifier(nb);
1348 if (err)
1349 dev_err(dev,
1350 "Failed to unregister switchdev blocking notifier (%d)\n", err);
1351
1296 err = unregister_switchdev_notifier(&port_switchdev_nb); 1352 err = unregister_switchdev_notifier(&port_switchdev_nb);
1297 if (err) 1353 if (err)
1298 dev_err(dev, 1354 dev_err(dev,
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 7b371e7c4bc6..866b6d148b77 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -95,8 +95,8 @@ struct switchdev_obj_port_vlan {
95 u16 vid_end; 95 u16 vid_end;
96}; 96};
97 97
98#define SWITCHDEV_OBJ_PORT_VLAN(obj) \ 98#define SWITCHDEV_OBJ_PORT_VLAN(OBJ) \
99 container_of(obj, struct switchdev_obj_port_vlan, obj) 99 container_of((OBJ), struct switchdev_obj_port_vlan, obj)
100 100
101/* SWITCHDEV_OBJ_ID_PORT_MDB */ 101/* SWITCHDEV_OBJ_ID_PORT_MDB */
102struct switchdev_obj_port_mdb { 102struct switchdev_obj_port_mdb {
@@ -105,8 +105,8 @@ struct switchdev_obj_port_mdb {
105 u16 vid; 105 u16 vid;
106}; 106};
107 107
108#define SWITCHDEV_OBJ_PORT_MDB(obj) \ 108#define SWITCHDEV_OBJ_PORT_MDB(OBJ) \
109 container_of(obj, struct switchdev_obj_port_mdb, obj) 109 container_of((OBJ), struct switchdev_obj_port_mdb, obj)
110 110
111void switchdev_trans_item_enqueue(struct switchdev_trans *trans, 111void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
112 void *data, void (*destructor)(void const *), 112 void *data, void (*destructor)(void const *),
@@ -121,10 +121,6 @@ typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj);
121 * @switchdev_port_attr_get: Get a port attribute (see switchdev_attr). 121 * @switchdev_port_attr_get: Get a port attribute (see switchdev_attr).
122 * 122 *
123 * @switchdev_port_attr_set: Set a port attribute (see switchdev_attr). 123 * @switchdev_port_attr_set: Set a port attribute (see switchdev_attr).
124 *
125 * @switchdev_port_obj_add: Add an object to port (see switchdev_obj_*).
126 *
127 * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj_*).
128 */ 124 */
129struct switchdev_ops { 125struct switchdev_ops {
130 int (*switchdev_port_attr_get)(struct net_device *dev, 126 int (*switchdev_port_attr_get)(struct net_device *dev,
@@ -132,11 +128,6 @@ struct switchdev_ops {
132 int (*switchdev_port_attr_set)(struct net_device *dev, 128 int (*switchdev_port_attr_set)(struct net_device *dev,
133 const struct switchdev_attr *attr, 129 const struct switchdev_attr *attr,
134 struct switchdev_trans *trans); 130 struct switchdev_trans *trans);
135 int (*switchdev_port_obj_add)(struct net_device *dev,
136 const struct switchdev_obj *obj,
137 struct switchdev_trans *trans);
138 int (*switchdev_port_obj_del)(struct net_device *dev,
139 const struct switchdev_obj *obj);
140}; 131};
141 132
142enum switchdev_notifier_type { 133enum switchdev_notifier_type {
@@ -146,6 +137,9 @@ enum switchdev_notifier_type {
146 SWITCHDEV_FDB_DEL_TO_DEVICE, 137 SWITCHDEV_FDB_DEL_TO_DEVICE,
147 SWITCHDEV_FDB_OFFLOADED, 138 SWITCHDEV_FDB_OFFLOADED,
148 139
140 SWITCHDEV_PORT_OBJ_ADD, /* Blocking. */
141 SWITCHDEV_PORT_OBJ_DEL, /* Blocking. */
142
149 SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE, 143 SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE,
150 SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE, 144 SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE,
151 SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE, 145 SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
@@ -165,6 +159,13 @@ struct switchdev_notifier_fdb_info {
165 offloaded:1; 159 offloaded:1;
166}; 160};
167 161
162struct switchdev_notifier_port_obj_info {
163 struct switchdev_notifier_info info; /* must be first */
164 const struct switchdev_obj *obj;
165 struct switchdev_trans *trans;
166 bool handled;
167};
168
168static inline struct net_device * 169static inline struct net_device *
169switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info) 170switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info)
170{ 171{
@@ -182,10 +183,17 @@ int switchdev_port_obj_add(struct net_device *dev,
182 const struct switchdev_obj *obj); 183 const struct switchdev_obj *obj);
183int switchdev_port_obj_del(struct net_device *dev, 184int switchdev_port_obj_del(struct net_device *dev,
184 const struct switchdev_obj *obj); 185 const struct switchdev_obj *obj);
186
185int register_switchdev_notifier(struct notifier_block *nb); 187int register_switchdev_notifier(struct notifier_block *nb);
186int unregister_switchdev_notifier(struct notifier_block *nb); 188int unregister_switchdev_notifier(struct notifier_block *nb);
187int call_switchdev_notifiers(unsigned long val, struct net_device *dev, 189int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
188 struct switchdev_notifier_info *info); 190 struct switchdev_notifier_info *info);
191
192int register_switchdev_blocking_notifier(struct notifier_block *nb);
193int unregister_switchdev_blocking_notifier(struct notifier_block *nb);
194int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
195 struct switchdev_notifier_info *info);
196
189void switchdev_port_fwd_mark_set(struct net_device *dev, 197void switchdev_port_fwd_mark_set(struct net_device *dev,
190 struct net_device *group_dev, 198 struct net_device *group_dev,
191 bool joining); 199 bool joining);
@@ -193,6 +201,18 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
193bool switchdev_port_same_parent_id(struct net_device *a, 201bool switchdev_port_same_parent_id(struct net_device *a,
194 struct net_device *b); 202 struct net_device *b);
195 203
204int switchdev_handle_port_obj_add(struct net_device *dev,
205 struct switchdev_notifier_port_obj_info *port_obj_info,
206 bool (*check_cb)(const struct net_device *dev),
207 int (*add_cb)(struct net_device *dev,
208 const struct switchdev_obj *obj,
209 struct switchdev_trans *trans));
210int switchdev_handle_port_obj_del(struct net_device *dev,
211 struct switchdev_notifier_port_obj_info *port_obj_info,
212 bool (*check_cb)(const struct net_device *dev),
213 int (*del_cb)(struct net_device *dev,
214 const struct switchdev_obj *obj));
215
196#define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops)) 216#define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops))
197#else 217#else
198 218
@@ -241,12 +261,53 @@ static inline int call_switchdev_notifiers(unsigned long val,
241 return NOTIFY_DONE; 261 return NOTIFY_DONE;
242} 262}
243 263
264static inline int
265register_switchdev_blocking_notifier(struct notifier_block *nb)
266{
267 return 0;
268}
269
270static inline int
271unregister_switchdev_blocking_notifier(struct notifier_block *nb)
272{
273 return 0;
274}
275
276static inline int
277call_switchdev_blocking_notifiers(unsigned long val,
278 struct net_device *dev,
279 struct switchdev_notifier_info *info)
280{
281 return NOTIFY_DONE;
282}
283
244static inline bool switchdev_port_same_parent_id(struct net_device *a, 284static inline bool switchdev_port_same_parent_id(struct net_device *a,
245 struct net_device *b) 285 struct net_device *b)
246{ 286{
247 return false; 287 return false;
248} 288}
249 289
290static inline int
291switchdev_handle_port_obj_add(struct net_device *dev,
292 struct switchdev_notifier_port_obj_info *port_obj_info,
293 bool (*check_cb)(const struct net_device *dev),
294 int (*add_cb)(struct net_device *dev,
295 const struct switchdev_obj *obj,
296 struct switchdev_trans *trans))
297{
298 return 0;
299}
300
301static inline int
302switchdev_handle_port_obj_del(struct net_device *dev,
303 struct switchdev_notifier_port_obj_info *port_obj_info,
304 bool (*check_cb)(const struct net_device *dev),
305 int (*del_cb)(struct net_device *dev,
306 const struct switchdev_obj *obj))
307{
308 return 0;
309}
310
250#define SWITCHDEV_SET_OPS(netdev, ops) do {} while (0) 311#define SWITCHDEV_SET_OPS(netdev, ops) do {} while (0)
251 312
252#endif 313#endif
diff --git a/net/dsa/port.c b/net/dsa/port.c
index ed0595459df1..2d7e01b23572 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -252,9 +252,6 @@ int dsa_port_vlan_add(struct dsa_port *dp,
252 .vlan = vlan, 252 .vlan = vlan,
253 }; 253 };
254 254
255 if (netif_is_bridge_master(vlan->obj.orig_dev))
256 return -EOPNOTSUPP;
257
258 if (br_vlan_enabled(dp->bridge_dev)) 255 if (br_vlan_enabled(dp->bridge_dev))
259 return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); 256 return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
260 257
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 7d0c19e7edcf..268119cf7117 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1050,8 +1050,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
1050static const struct switchdev_ops dsa_slave_switchdev_ops = { 1050static const struct switchdev_ops dsa_slave_switchdev_ops = {
1051 .switchdev_port_attr_get = dsa_slave_port_attr_get, 1051 .switchdev_port_attr_get = dsa_slave_port_attr_get,
1052 .switchdev_port_attr_set = dsa_slave_port_attr_set, 1052 .switchdev_port_attr_set = dsa_slave_port_attr_set,
1053 .switchdev_port_obj_add = dsa_slave_port_obj_add,
1054 .switchdev_port_obj_del = dsa_slave_port_obj_del,
1055}; 1053};
1056 1054
1057static struct device_type dsa_type = { 1055static struct device_type dsa_type = {
@@ -1557,6 +1555,44 @@ err_fdb_work_init:
1557 return NOTIFY_BAD; 1555 return NOTIFY_BAD;
1558} 1556}
1559 1557
1558static int
1559dsa_slave_switchdev_port_obj_event(unsigned long event,
1560 struct net_device *netdev,
1561 struct switchdev_notifier_port_obj_info *port_obj_info)
1562{
1563 int err = -EOPNOTSUPP;
1564
1565 switch (event) {
1566 case SWITCHDEV_PORT_OBJ_ADD:
1567 err = dsa_slave_port_obj_add(netdev, port_obj_info->obj,
1568 port_obj_info->trans);
1569 break;
1570 case SWITCHDEV_PORT_OBJ_DEL:
1571 err = dsa_slave_port_obj_del(netdev, port_obj_info->obj);
1572 break;
1573 }
1574
1575 port_obj_info->handled = true;
1576 return notifier_from_errno(err);
1577}
1578
1579static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
1580 unsigned long event, void *ptr)
1581{
1582 struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
1583
1584 if (!dsa_slave_dev_check(dev))
1585 return NOTIFY_DONE;
1586
1587 switch (event) {
1588 case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
1589 case SWITCHDEV_PORT_OBJ_DEL:
1590 return dsa_slave_switchdev_port_obj_event(event, dev, ptr);
1591 }
1592
1593 return NOTIFY_DONE;
1594}
1595
1560static struct notifier_block dsa_slave_nb __read_mostly = { 1596static struct notifier_block dsa_slave_nb __read_mostly = {
1561 .notifier_call = dsa_slave_netdevice_event, 1597 .notifier_call = dsa_slave_netdevice_event,
1562}; 1598};
@@ -1565,8 +1601,13 @@ static struct notifier_block dsa_slave_switchdev_notifier = {
1565 .notifier_call = dsa_slave_switchdev_event, 1601 .notifier_call = dsa_slave_switchdev_event,
1566}; 1602};
1567 1603
1604static struct notifier_block dsa_slave_switchdev_blocking_notifier = {
1605 .notifier_call = dsa_slave_switchdev_blocking_event,
1606};
1607
1568int dsa_slave_register_notifier(void) 1608int dsa_slave_register_notifier(void)
1569{ 1609{
1610 struct notifier_block *nb;
1570 int err; 1611 int err;
1571 1612
1572 err = register_netdevice_notifier(&dsa_slave_nb); 1613 err = register_netdevice_notifier(&dsa_slave_nb);
@@ -1577,8 +1618,15 @@ int dsa_slave_register_notifier(void)
1577 if (err) 1618 if (err)
1578 goto err_switchdev_nb; 1619 goto err_switchdev_nb;
1579 1620
1621 nb = &dsa_slave_switchdev_blocking_notifier;
1622 err = register_switchdev_blocking_notifier(nb);
1623 if (err)
1624 goto err_switchdev_blocking_nb;
1625
1580 return 0; 1626 return 0;
1581 1627
1628err_switchdev_blocking_nb:
1629 unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
1582err_switchdev_nb: 1630err_switchdev_nb:
1583 unregister_netdevice_notifier(&dsa_slave_nb); 1631 unregister_netdevice_notifier(&dsa_slave_nb);
1584 return err; 1632 return err;
@@ -1586,8 +1634,14 @@ err_switchdev_nb:
1586 1634
1587void dsa_slave_unregister_notifier(void) 1635void dsa_slave_unregister_notifier(void)
1588{ 1636{
1637 struct notifier_block *nb;
1589 int err; 1638 int err;
1590 1639
1640 nb = &dsa_slave_switchdev_blocking_notifier;
1641 err = unregister_switchdev_blocking_notifier(nb);
1642 if (err)
1643 pr_err("DSA: failed to unregister switchdev blocking notifier (%d)\n", err);
1644
1591 err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier); 1645 err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
1592 if (err) 1646 if (err)
1593 pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err); 1647 pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err);
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);