diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/bridge/br_if.c | 13 | ||||
| -rw-r--r-- | net/bridge/br_private.h | 13 | ||||
| -rw-r--r-- | net/bridge/br_switchdev.c | 82 | ||||
| -rw-r--r-- | net/dsa/port.c | 16 |
4 files changed, 90 insertions, 34 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index c0df50e4abbb..86f6d7e93ea8 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -349,7 +349,6 @@ static void del_nbp(struct net_bridge_port *p) nbp_backup_clear(p); nbp_update_port_count(br); - nbp_switchdev_del(p); netdev_upper_dev_unlink(dev, br->dev); @@ -644,10 +643,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, if (err) goto err5; - err = nbp_switchdev_add(p); - if (err) - goto err6; - dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); @@ -685,13 +680,13 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, */ err = dev_pre_changeaddr_notify(br->dev, dev->dev_addr, extack); if (err) - goto err7; + goto err6; } err = nbp_vlan_init(p, extack); if (err) { netdev_err(dev, "failed to initialize vlan filtering on this port\n"); - goto err7; + goto err6; } spin_lock_bh(&br->lock); @@ -714,14 +709,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, return 0; -err7: +err6: if (fdb_synced) br_fdb_unsync_static(br, p); list_del_rcu(&p->list); br_fdb_delete_by_port(br, p, 0, 1); nbp_update_port_count(br); - nbp_switchdev_del(p); -err6: netdev_upper_dev_unlink(dev, br->dev); err5: dev->priv_flags &= ~IFF_BRIDGE_PORT; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index d0e6ae25a4e4..fbf73a5256d0 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -392,6 +392,8 @@ struct net_bridge_port { * hardware domain. */ int hwdom; + int offload_count; + struct netdev_phys_item_id ppid; #endif u16 group_fwd_mask; u16 backup_redirected_cnt; @@ -1857,8 +1859,6 @@ void br_switchdev_fdb_notify(struct net_bridge *br, int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, struct netlink_ext_ack *extack); int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid); -int nbp_switchdev_add(struct net_bridge_port *p); -void nbp_switchdev_del(struct net_bridge_port *p); void br_switchdev_init(struct net_bridge *br); static inline void br_switchdev_frame_unmark(struct sk_buff *skb) @@ -1907,15 +1907,6 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb) { } -static inline int nbp_switchdev_add(struct net_bridge_port *p) -{ - return 0; -} - -static inline void nbp_switchdev_del(struct net_bridge_port *p) -{ -} - static inline void br_switchdev_init(struct net_bridge *br) { } diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index f3120f13c293..39f0787fde01 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -133,7 +133,7 @@ static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining) /* joining is yet to be added to the port list. */ list_for_each_entry(p, &br->port_list, list) { - if (netdev_port_same_parent_id(joining->dev, p->dev)) { + if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) { joining->hwdom = p->hwdom; return 0; } @@ -162,27 +162,85 @@ static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving) clear_bit(leaving->hwdom, &br->busy_hwdoms); } -int nbp_switchdev_add(struct net_bridge_port *p) +static int nbp_switchdev_add(struct net_bridge_port *p, + struct netdev_phys_item_id ppid, + struct netlink_ext_ack *extack) { - struct netdev_phys_item_id ppid = { }; - int err; + if (p->offload_count) { + /* Prevent unsupported configurations such as a bridge port + * which is a bonding interface, and the member ports are from + * different hardware switches. + */ + if (!netdev_phys_item_id_same(&p->ppid, &ppid)) { + NL_SET_ERR_MSG_MOD(extack, + "Same bridge port cannot be offloaded by two physical switches"); + return -EBUSY; + } - ASSERT_RTNL(); + /* Tolerate drivers that call switchdev_bridge_port_offload() + * more than once for the same bridge port, such as when the + * bridge port is an offloaded bonding/team interface. + */ + p->offload_count++; - err = dev_get_port_parent_id(p->dev, &ppid, true); - if (err) { - if (err == -EOPNOTSUPP) - return 0; - return err; + return 0; } + p->ppid = ppid; + p->offload_count = 1; + return nbp_switchdev_hwdom_set(p); } -void nbp_switchdev_del(struct net_bridge_port *p) +static void nbp_switchdev_del(struct net_bridge_port *p) { - ASSERT_RTNL(); + if (WARN_ON(!p->offload_count)) + return; + + p->offload_count--; + + if (p->offload_count) + return; if (p->hwdom) nbp_switchdev_hwdom_put(p); } + +/* Let the bridge know that this port is offloaded, so that it can assign a + * switchdev hardware domain to it. + */ +int switchdev_bridge_port_offload(struct net_device *brport_dev, + struct net_device *dev, + struct netlink_ext_ack *extack) +{ + struct netdev_phys_item_id ppid; + struct net_bridge_port *p; + int err; + + ASSERT_RTNL(); + + p = br_port_get_rtnl(brport_dev); + if (!p) + return -ENODEV; + + err = dev_get_port_parent_id(dev, &ppid, false); + if (err) + return err; + + return nbp_switchdev_add(p, ppid, extack); +} +EXPORT_SYMBOL_GPL(switchdev_bridge_port_offload); + +void switchdev_bridge_port_unoffload(struct net_device *brport_dev) +{ + struct net_bridge_port *p; + + ASSERT_RTNL(); + + p = br_port_get_rtnl(brport_dev); + if (!p) + return; + + nbp_switchdev_del(p); +} +EXPORT_SYMBOL_GPL(switchdev_bridge_port_unoffload); diff --git a/net/dsa/port.c b/net/dsa/port.c index 982e18771d76..7accda066149 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -292,6 +292,8 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, .port = dp->index, .br = br, }; + struct net_device *dev = dp->slave; + struct net_device *brport_dev; int err; /* Here the interface is already bridged. Reflect the current @@ -299,16 +301,24 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br, */ dp->bridge_dev = br; + brport_dev = dsa_port_to_bridge_port(dp); + err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info); if (err) goto out_rollback; - err = dsa_port_switchdev_sync(dp, extack); + err = switchdev_bridge_port_offload(brport_dev, dev, extack); if (err) goto out_rollback_unbridge; + err = dsa_port_switchdev_sync(dp, extack); + if (err) + goto out_rollback_unoffload; + return 0; +out_rollback_unoffload: + switchdev_bridge_port_unoffload(brport_dev); out_rollback_unbridge: dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info); out_rollback: @@ -319,6 +329,10 @@ out_rollback: int dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br, struct netlink_ext_ack *extack) { + struct net_device *brport_dev = dsa_port_to_bridge_port(dp); + + switchdev_bridge_port_unoffload(brport_dev); + return dsa_port_switchdev_unsync_objs(dp, br, extack); } |
