diff --git a/include/net/dsa.h b/include/net/dsa.h index 7f3d2eeb34f474..8dcd1a53fdb46a 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -243,6 +243,7 @@ struct dsa_bridge { refcount_t refcount; u8 tx_fwd_offload:1; u8 have_foreign:1; + u8 promisc:1; }; struct dsa_port { diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index d610776ecd7634..9b868a7c34598d 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -321,6 +321,7 @@ int dsa_slave_change_mtu(struct net_device *dev, int new_mtu); int dsa_slave_manage_vlan_filtering(struct net_device *dev, bool vlan_filtering); int dsa_bridge_foreign_dev_update(struct net_device *bridge_dev); +int dsa_bridge_promisc_update(struct net_device *bridge_dev); static inline struct dsa_port *dsa_slave_to_port(const struct net_device *dev) { diff --git a/net/dsa/port.c b/net/dsa/port.c index 7aeabd8a625200..74c386fbc5a360 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -606,8 +606,13 @@ int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, if (err) goto err_foreign_update; + err = dsa_bridge_promisc_update(bridge_dev); + if (err) + goto err_promisc_update; + return 0; +err_promisc_update: err_foreign_update: dsa_port_pre_bridge_leave(dp, bridge_dev); dsa_port_bridge_leave(dp, bridge_dev); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 3293ca4e90c259..ae116b82332531 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -2775,6 +2775,13 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, } case NETDEV_CHANGE: case NETDEV_UP: { + int err; + + if (netif_is_bridge_master(dev)) { + err = dsa_bridge_promisc_update(dev); + return notifier_from_errno(err); + } + /* Track state of master port. * DSA driver may require the master port (and indirectly * the tagger) to be available for some special operation. @@ -2913,13 +2920,13 @@ static bool dsa_foreign_dev_check(const struct net_device *dev, } /* We need to keep flooding towards the CPU enabled as long as software - * forwarding is required. + * forwarding is required, or the bridge device is promiscuous. */ static void dsa_bridge_host_flood_change(struct dsa_bridge *bridge, - bool have_foreign) + bool have_foreign, bool promisc) { - bool host_flood_was_enabled = bridge->have_foreign; - bool host_flood_enabled = have_foreign; + bool host_flood_was_enabled = bridge->have_foreign || bridge->promisc; + bool host_flood_enabled = have_foreign || promisc; int inc = host_flood_enabled ? 1 : -1; struct net_device *brport_dev; struct dsa_switch_tree *dst; @@ -2939,6 +2946,7 @@ static void dsa_bridge_host_flood_change(struct dsa_bridge *bridge, out: bridge->have_foreign = have_foreign; + bridge->promisc = promisc; } int dsa_bridge_foreign_dev_update(struct net_device *bridge_dev) @@ -2971,7 +2979,32 @@ int dsa_bridge_foreign_dev_update(struct net_device *bridge_dev) } } - dsa_bridge_host_flood_change(bridge, have_foreign); + dsa_bridge_host_flood_change(bridge, have_foreign, bridge->promisc); + + return 0; +} + +int dsa_bridge_promisc_update(struct net_device *bridge_dev) +{ + struct dsa_bridge *bridge = NULL; + struct dsa_switch_tree *dst; + struct dsa_port *dp; + + list_for_each_entry(dst, &dsa_tree_list, list) { + dsa_tree_for_each_user_port(dp, dst) { + if (dsa_port_offloads_bridge_dev(dp, bridge_dev)) { + bridge = dp->bridge; + break; + } + } + } + + /* Bridge with no DSA interface in it */ + if (!bridge) + return 0; + + dsa_bridge_host_flood_change(bridge, bridge->have_foreign, + bridge_dev->flags & IFF_PROMISC); return 0; }