From bfbab3104413081907ad12dbec433f50fe3588cd Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 20 Oct 2021 20:58:51 +0300 Subject: [PATCH] net: mscc: ocelot: add the local station MAC addresses in VID 0 The ocelot switchdev driver does not include the CPU port in the list of flooding destinations for unknown traffic, instead that traffic is supposed to match FDB entries to reach the CPU. The addresses it installs are: (a) the station MAC address, in ocelot_probe_port() and later during runtime in ocelot_port_set_mac_address(). These are the VLAN-unaware addresses. The VLAN-aware addresses are in ocelot_vlan_vid_add(). (b) multicast addresses added with dev_mc_add() (not bridge host MDB entries) in ocelot_mc_sync() (c) multicast destination MAC addresses for MRP in ocelot_mrp_save_mac(), to make sure those are dropped (not forwarded) by the bridging service, just trapped to the CPU So we can see that the logic is slightly buggy ever since the initial commit a556c76adc05 ("net: mscc: Add initial Ocelot switch support"). This is because, when ocelot_probe_port() runs, the port pvid is 0. Then we join a VLAN-aware bridge, the pvid becomes 1, we call ocelot_port_set_mac_address(), this learns the new MAC address in VID 1 (also fails to forget the old one, since it thinks it's in VID 1, but that's not so important). Then when we leave the VLAN-aware bridge, outside world is unable to ping our new MAC address because it isn't learned in VID 0, the VLAN-unaware pvid. [ note: this is strictly based on static analysis, I don't have hardware to test. But there are also many more corner cases ] The basic idea is that we should have a separation of concerns, and the FDB entries used for standalone operation should be managed by the driver, and the FDB entries used by the bridging service should be managed by the bridge. So the standalone and VLAN-unaware bridge FDB entries should not follow the bridge PVID, because that will only be active when the bridge is VLAN-aware. So since the port pvid is coincidentally zero during probe time, just make those entries statically go to VID 0. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 11 ++++++----- drivers/net/ethernet/mscc/ocelot.h | 1 + drivers/net/ethernet/mscc/ocelot_mrp.c | 8 ++++---- drivers/net/ethernet/mscc/ocelot_net.c | 12 ++++++------ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index bc033e62be97..30aa99a95005 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -268,7 +268,7 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, ocelot_port->pvid_vlan = pvid_vlan; if (!ocelot_port->vlan_aware) - pvid_vlan.vid = 0; + pvid_vlan.vid = OCELOT_VLAN_UNAWARE_PVID; ocelot_rmw_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(pvid_vlan.vid), @@ -501,7 +501,7 @@ static void ocelot_vlan_init(struct ocelot *ocelot) * traffic. It is added automatically if 8021q module is loaded, but * we can't rely on it since module may be not loaded. */ - ocelot_vlant_set_mask(ocelot, 0, all_ports); + ocelot_vlant_set_mask(ocelot, OCELOT_VLAN_UNAWARE_PVID, all_ports); /* Set vlan ingress filter mask to all ports but the CPU port by * default. @@ -2194,9 +2194,10 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot) OCELOT_TAG_PREFIX_NONE); /* Configure the CPU port to be VLAN aware */ - ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) | - ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | - ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), + ocelot_write_gix(ocelot, + ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_VLAN_UNAWARE_PVID) | + ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | + ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), ANA_PORT_VLAN_CFG, cpu); } diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index 1952d6a1b98a..e43da09b8f91 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -25,6 +25,7 @@ #include "ocelot_rew.h" #include "ocelot_qs.h" +#define OCELOT_VLAN_UNAWARE_PVID 0 #define OCELOT_BUFFER_CELL_SZ 60 #define OCELOT_STATS_CHECK_DELAY (2 * HZ) diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index 4b0941f09f71..1fa58546abdc 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -116,16 +116,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot, struct ocelot_port *port) { ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac, - port->pvid_vlan.vid, ENTRYTYPE_LOCKED); + OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac, - port->pvid_vlan.vid, ENTRYTYPE_LOCKED); + OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); } static void ocelot_mrp_del_mac(struct ocelot *ocelot, struct ocelot_port *port) { - ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid); - ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid); + ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID); + ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID); } int ocelot_mrp_add(struct ocelot *ocelot, int port, diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index affa9649f490..e3fc4548f642 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -418,7 +418,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) * with VLAN filtering feature. We need to keep it to receive * untagged traffic. */ - if (vid == 0) + if (vid == OCELOT_VLAN_UNAWARE_PVID) return 0; ret = ocelot_vlan_del(ocelot, port, vid); @@ -553,7 +553,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.forget.addr, addr); - w.forget.vid = ocelot_port->pvid_vlan.vid; + w.forget.vid = OCELOT_VLAN_UNAWARE_PVID; w.type = OCELOT_MACT_FORGET; return ocelot_enqueue_mact_action(ocelot, &w); @@ -567,7 +567,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.learn.addr, addr); - w.learn.vid = ocelot_port->pvid_vlan.vid; + w.learn.vid = OCELOT_VLAN_UNAWARE_PVID; w.learn.pgid = PGID_CPU; w.learn.entry_type = ENTRYTYPE_LOCKED; w.type = OCELOT_MACT_LEARN; @@ -602,9 +602,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) /* Learn the new net device MAC address in the mac table. */ ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, - ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); + OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); /* Then forget the previous one. */ - ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid_vlan.vid); + ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID); eth_hw_addr_set(dev, addr->sa_data); return 0; @@ -1707,7 +1707,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, eth_hw_addr_gen(dev, ocelot->base_mac, port); ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, - ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED); + OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); ocelot_init_port(ocelot, port);