Commit b8e997c4 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: dsa: introduce a separate cross-chip notifier type for host MDBs

Commit abd49535 ("net: dsa: execute dsa_switch_mdb_add only for
routing port in cross-chip topologies") does a surprisingly good job
even for the SWITCHDEV_OBJ_ID_HOST_MDB use case, where DSA simply
translates a switchdev object received on dp into a cross-chip notifier
for dp->cpu_dp.

To visualize how that works, imagine the daisy chain topology below and
consider a SWITCHDEV_OBJ_ID_HOST_MDB object emitted on sw2p0. How does
the cross-chip notifier know to match on all the right ports (sw0p4, the
dedicated CPU port, sw1p4, an upstream DSA link, and sw2p4, another
upstream DSA link)?

                                                |
       sw0p0     sw0p1     sw0p2     sw0p3     sw0p4
    [  user ] [  user ] [  user ] [  dsa  ] [  cpu  ]
    [       ] [       ] [       ] [       ] [   x   ]
                                      |
                                      +---------+
                                                |
       sw1p0     sw1p1     sw1p2     sw1p3     sw1p4
    [  user ] [  user ] [  user ] [  dsa  ] [  dsa  ]
    [       ] [       ] [       ] [       ] [   x   ]
                                      |
                                      +---------+
                                                |
       sw2p0     sw2p1     sw2p2     sw2p3     sw2p4
    [  user ] [  user ] [  user ] [  user ] [  dsa  ]
    [       ] [       ] [       ] [       ] [   x   ]

The answer is simple: the dedicated CPU port of sw2p0 is sw0p4, and
dsa_routing_port returns the upstream port for all switches.

That is fine, but there are other topologies where this does not work as
well. There are trees with "H" topologies in the wild, where there are 2
or more switches with DSA links between them, but every switch has its
dedicated CPU port. For these topologies, it seems stupid for the neighbor
switches to install an MDB entry on the routing port, since these
multicast addresses are fundamentally different than the usual ones we
support (and that is the justification for this patch, to introduce the
concept of a termination plane multicast MAC address, as opposed to a
forwarding plane multicast MAC address).

For example, when a SWITCHDEV_OBJ_ID_HOST_MDB would get added to sw0p0,
without this patch, it would get treated as a regular port MDB on sw0p2
and it would match on the ports below (including the sw1p3 routing port).

                         |                                  |
    sw0p0     sw0p1     sw0p2     sw0p3          sw1p3     sw1p2     sw1p1     sw1p0
 [  user ] [  user ] [  cpu  ] [  dsa  ]      [  dsa  ] [  cpu  ] [  user ] [  user ]
 [       ] [       ] [   x   ] [       ] ---- [   x   ] [       ] [       ] [       ]

With the patch, the host MDB notifier on sw0p0 matches only on the local
switch, which is what we want for a termination plane address.

                         |                                  |
    sw0p0     sw0p1     sw0p2     sw0p3          sw1p3     sw1p2     sw1p1     sw1p0
 [  user ] [  user ] [  cpu  ] [  dsa  ]      [  dsa  ] [  cpu  ] [  user ] [  user ]
 [       ] [       ] [   x   ] [       ] ---- [       ] [       ] [       ] [       ]

Name this new matching function "dsa_switch_host_address_match" since we
will be reusing it soon for host FDB entries as well.
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 63609c8f
...@@ -27,6 +27,8 @@ enum { ...@@ -27,6 +27,8 @@ enum {
DSA_NOTIFIER_LAG_LEAVE, DSA_NOTIFIER_LAG_LEAVE,
DSA_NOTIFIER_MDB_ADD, DSA_NOTIFIER_MDB_ADD,
DSA_NOTIFIER_MDB_DEL, DSA_NOTIFIER_MDB_DEL,
DSA_NOTIFIER_HOST_MDB_ADD,
DSA_NOTIFIER_HOST_MDB_DEL,
DSA_NOTIFIER_VLAN_ADD, DSA_NOTIFIER_VLAN_ADD,
DSA_NOTIFIER_VLAN_DEL, DSA_NOTIFIER_VLAN_DEL,
DSA_NOTIFIER_MTU, DSA_NOTIFIER_MTU,
...@@ -214,6 +216,10 @@ int dsa_port_mdb_add(const struct dsa_port *dp, ...@@ -214,6 +216,10 @@ int dsa_port_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb); const struct switchdev_obj_port_mdb *mdb);
int dsa_port_mdb_del(const struct dsa_port *dp, int dsa_port_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb); const struct switchdev_obj_port_mdb *mdb);
int dsa_port_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_pre_bridge_flags(const struct dsa_port *dp, int dsa_port_pre_bridge_flags(const struct dsa_port *dp,
struct switchdev_brport_flags flags, struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
......
...@@ -681,6 +681,30 @@ int dsa_port_mdb_del(const struct dsa_port *dp, ...@@ -681,6 +681,30 @@ int dsa_port_mdb_del(const struct dsa_port *dp,
return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
} }
int dsa_port_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
{
struct dsa_notifier_mdb_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
.mdb = mdb,
};
return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info);
}
int dsa_port_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
{
struct dsa_notifier_mdb_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
.mdb = mdb,
};
return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info);
}
int dsa_port_vlan_add(struct dsa_port *dp, int dsa_port_vlan_add(struct dsa_port *dp,
const struct switchdev_obj_port_vlan *vlan, const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
......
...@@ -418,10 +418,7 @@ static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx, ...@@ -418,10 +418,7 @@ static int dsa_slave_port_obj_add(struct net_device *dev, const void *ctx,
if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* DSA can directly translate this to a normal MDB add, err = dsa_port_host_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
* but on the CPU port.
*/
err = dsa_port_mdb_add(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
break; break;
case SWITCHDEV_OBJ_ID_PORT_VLAN: case SWITCHDEV_OBJ_ID_PORT_VLAN:
if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
...@@ -495,10 +492,7 @@ static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx, ...@@ -495,10 +492,7 @@ static int dsa_slave_port_obj_del(struct net_device *dev, const void *ctx,
if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* DSA can directly translate this to a normal MDB add, err = dsa_port_host_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
* but on the CPU port.
*/
err = dsa_port_mdb_del(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
break; break;
case SWITCHDEV_OBJ_ID_PORT_VLAN: case SWITCHDEV_OBJ_ID_PORT_VLAN:
if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
......
...@@ -154,6 +154,27 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, ...@@ -154,6 +154,27 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
return 0; return 0;
} }
/* Matches for all upstream-facing ports (the CPU port and all upstream-facing
* DSA links) that sit between the targeted port on which the notifier was
* emitted and its dedicated CPU port.
*/
static bool dsa_switch_host_address_match(struct dsa_switch *ds, int port,
int info_sw_index, int info_port)
{
struct dsa_port *targeted_dp, *cpu_dp;
struct dsa_switch *targeted_ds;
targeted_ds = dsa_switch_find(ds->dst->index, info_sw_index);
targeted_dp = dsa_to_port(targeted_ds, info_port);
cpu_dp = targeted_dp->cpu_dp;
if (dsa_switch_is_upstream_of(ds, targeted_ds))
return port == dsa_towards_port(ds, cpu_dp->ds->index,
cpu_dp->index);
return false;
}
static int dsa_switch_fdb_add(struct dsa_switch *ds, static int dsa_switch_fdb_add(struct dsa_switch *ds,
struct dsa_notifier_fdb_info *info) struct dsa_notifier_fdb_info *info)
{ {
...@@ -258,6 +279,39 @@ static int dsa_switch_mdb_del(struct dsa_switch *ds, ...@@ -258,6 +279,39 @@ static int dsa_switch_mdb_del(struct dsa_switch *ds,
return 0; return 0;
} }
static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
struct dsa_notifier_mdb_info *info)
{
int err = 0;
int port;
if (!ds->ops->port_mdb_add)
return -EOPNOTSUPP;
for (port = 0; port < ds->num_ports; port++) {
if (dsa_switch_host_address_match(ds, port, info->sw_index,
info->port)) {
err = ds->ops->port_mdb_add(ds, port, info->mdb);
if (err)
break;
}
}
return err;
}
static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
struct dsa_notifier_mdb_info *info)
{
if (!ds->ops->port_mdb_del)
return -EOPNOTSUPP;
if (ds->index == info->sw_index)
return ds->ops->port_mdb_del(ds, info->port, info->mdb);
return 0;
}
static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port, static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
struct dsa_notifier_vlan_info *info) struct dsa_notifier_vlan_info *info)
{ {
...@@ -441,6 +495,12 @@ static int dsa_switch_event(struct notifier_block *nb, ...@@ -441,6 +495,12 @@ static int dsa_switch_event(struct notifier_block *nb,
case DSA_NOTIFIER_MDB_DEL: case DSA_NOTIFIER_MDB_DEL:
err = dsa_switch_mdb_del(ds, info); err = dsa_switch_mdb_del(ds, info);
break; break;
case DSA_NOTIFIER_HOST_MDB_ADD:
err = dsa_switch_host_mdb_add(ds, info);
break;
case DSA_NOTIFIER_HOST_MDB_DEL:
err = dsa_switch_host_mdb_del(ds, info);
break;
case DSA_NOTIFIER_VLAN_ADD: case DSA_NOTIFIER_VLAN_ADD:
err = dsa_switch_vlan_add(ds, info); err = dsa_switch_vlan_add(ds, info);
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment