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

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 a556c76a ("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: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0da1a1c4
...@@ -268,7 +268,7 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, ...@@ -268,7 +268,7 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
ocelot_port->pvid_vlan = pvid_vlan; ocelot_port->pvid_vlan = pvid_vlan;
if (!ocelot_port->vlan_aware) if (!ocelot_port->vlan_aware)
pvid_vlan.vid = 0; pvid_vlan.vid = OCELOT_VLAN_UNAWARE_PVID;
ocelot_rmw_gix(ocelot, ocelot_rmw_gix(ocelot,
ANA_PORT_VLAN_CFG_VLAN_VID(pvid_vlan.vid), ANA_PORT_VLAN_CFG_VLAN_VID(pvid_vlan.vid),
...@@ -501,7 +501,7 @@ static void ocelot_vlan_init(struct ocelot *ocelot) ...@@ -501,7 +501,7 @@ static void ocelot_vlan_init(struct ocelot *ocelot)
* traffic. It is added automatically if 8021q module is loaded, but * traffic. It is added automatically if 8021q module is loaded, but
* we can't rely on it since module may be not loaded. * 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 /* Set vlan ingress filter mask to all ports but the CPU port by
* default. * default.
...@@ -2194,9 +2194,10 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot) ...@@ -2194,9 +2194,10 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot)
OCELOT_TAG_PREFIX_NONE); OCELOT_TAG_PREFIX_NONE);
/* Configure the CPU port to be VLAN aware */ /* 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_AWARE_ENA | ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_VLAN_UNAWARE_PVID) |
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
ANA_PORT_VLAN_CFG, cpu); ANA_PORT_VLAN_CFG, cpu);
} }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "ocelot_rew.h" #include "ocelot_rew.h"
#include "ocelot_qs.h" #include "ocelot_qs.h"
#define OCELOT_VLAN_UNAWARE_PVID 0
#define OCELOT_BUFFER_CELL_SZ 60 #define OCELOT_BUFFER_CELL_SZ 60
#define OCELOT_STATS_CHECK_DELAY (2 * HZ) #define OCELOT_STATS_CHECK_DELAY (2 * HZ)
......
...@@ -116,16 +116,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot, ...@@ -116,16 +116,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot,
struct ocelot_port *port) struct ocelot_port *port)
{ {
ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac, 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, 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, static void ocelot_mrp_del_mac(struct ocelot *ocelot,
struct ocelot_port *port) struct ocelot_port *port)
{ {
ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid); ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID);
ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid); ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID);
} }
int ocelot_mrp_add(struct ocelot *ocelot, int port, int ocelot_mrp_add(struct ocelot *ocelot, int port,
......
...@@ -418,7 +418,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) ...@@ -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 * with VLAN filtering feature. We need to keep it to receive
* untagged traffic. * untagged traffic.
*/ */
if (vid == 0) if (vid == OCELOT_VLAN_UNAWARE_PVID)
return 0; return 0;
ret = ocelot_vlan_del(ocelot, port, vid); ret = ocelot_vlan_del(ocelot, port, vid);
...@@ -553,7 +553,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) ...@@ -553,7 +553,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
struct ocelot_mact_work_ctx w; struct ocelot_mact_work_ctx w;
ether_addr_copy(w.forget.addr, addr); 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; w.type = OCELOT_MACT_FORGET;
return ocelot_enqueue_mact_action(ocelot, &w); return ocelot_enqueue_mact_action(ocelot, &w);
...@@ -567,7 +567,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) ...@@ -567,7 +567,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr)
struct ocelot_mact_work_ctx w; struct ocelot_mact_work_ctx w;
ether_addr_copy(w.learn.addr, addr); 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.pgid = PGID_CPU;
w.learn.entry_type = ENTRYTYPE_LOCKED; w.learn.entry_type = ENTRYTYPE_LOCKED;
w.type = OCELOT_MACT_LEARN; w.type = OCELOT_MACT_LEARN;
...@@ -602,9 +602,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) ...@@ -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. */ /* Learn the new net device MAC address in the mac table. */
ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, 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. */ /* 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); eth_hw_addr_set(dev, addr->sa_data);
return 0; return 0;
...@@ -1707,7 +1707,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, ...@@ -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); eth_hw_addr_gen(dev, ocelot->base_mac, port);
ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, 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); ocelot_init_port(ocelot, port);
......
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