Skip to content

Commit

Permalink
net: dsa: monitor changes to bridge promiscuity
Browse files Browse the repository at this point in the history
In preparation of changing the bridge such that it stops managing the
IFF_PROMISC flag on offloaded bridge ports, we need to ensure that DSA
preserves behavior in the circumstances that matter.

The bridge software data path implementation (br_handle_frame_finish)
passes a unicast frame up if a BR_FDB_LOCAL entry exists, or if the MAC
DA is unknown, if local_rcv is true. In turn, local_rcv is true when
the bridge device itself is promiscuous.

The analogous behavior in the offloaded plane is to enable flooding of
packets with unknown destination towards the CPU when the bridge device
itself is promiscuous. This change achieves that by monitoring
IFF_PROMISC changes on bridge devices, and calling
dsa_bridge_host_flood_change -> dsa_port_manage_cpu_flood on such changes.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
  • Loading branch information
vladimiroltean authored and intel-lab-lkp committed Apr 8, 2022
1 parent f60f0d2 commit 0c56b50
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 5 deletions.
1 change: 1 addition & 0 deletions include/net/dsa.h
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions net/dsa/dsa_priv.h
Expand Up @@ -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)
{
Expand Down
5 changes: 5 additions & 0 deletions net/dsa/port.c
Expand Up @@ -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);
Expand Down
43 changes: 38 additions & 5 deletions net/dsa/slave.c
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 0c56b50

Please sign in to comment.