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 a556c76adc ("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 <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vladimir Oltean 2021-10-20 20:58:51 +03:00 committed by David S. Miller
parent 0da1a1c489
commit bfbab31044
4 changed files with 17 additions and 15 deletions

View File

@ -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,7 +2194,8 @@ 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) |
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);

View File

@ -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)

View File

@ -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,

View File

@ -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);