OSDN Git Service

net: mscc: ocelot: add the local station MAC addresses in VID 0
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 20 Oct 2021 17:58:51 +0000 (20:58 +0300)
committerDavid S. Miller <davem@davemloft.net>
Thu, 21 Oct 2021 11:14:29 +0000 (12:14 +0100)
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 <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot.h
drivers/net/ethernet/mscc/ocelot_mrp.c
drivers/net/ethernet/mscc/ocelot_net.c

index bc033e6..30aa99a 100644 (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,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);
 }
 
index 1952d6a..e43da09 100644 (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)
index 4b0941f..1fa5854 100644 (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,
index affa964..e3fc454 100644 (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);