OSDN Git Service

Merge branch 'dsa-fdb-isolation'
authorDavid S. Miller <davem@davemloft.net>
Sun, 27 Feb 2022 11:06:14 +0000 (11:06 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 27 Feb 2022 11:06:14 +0000 (11:06 +0000)
Vladimir Oltean says:

====================
DSA FDB isolation

There are use cases which need FDB isolation between standalone ports
and bridged ports, as well as isolation between ports of different
bridges. Most of these use cases are a result of the fact that packets
can now be partially forwarded by the software bridge, so one port might
need to send a packet to the CPU but its FDB lookup will see that it can
forward it directly to a bridge port where that packet was autonomously
learned. So the source port will attempt to shortcircuit the CPU and
forward autonomously, which it can't due to the forwarding isolation we
have in place. So we will have packet drops instead of proper operation.

Additionally, before DSA can implement IFF_UNICAST_FLT for standalone
ports, we must have control over which database we install FDB entries
corresponding to port MAC addresses in. We don't want to hinder the
operation of the bridging layer.

DSA does not have a driver API that encourages FDB isolation, so this
needs to be created. The basis for this is a new struct dsa_db which
annotates each FDB and MDB entry with the database it belongs to.

The sja1105 and felix drivers are modified to observe the dsa_db
argument, and therefore, enforce the FDB isolation.

Compared to the previous RFC patch series from August:
https://patchwork.kernel.org/project/netdevbpf/cover/20210818120150.892647-1-vladimir.oltean@nxp.com/

what is different is that I stopped trying to make SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE
blocking, instead I'm making use of the fact that DSA waits for switchdev FDB work
items to finish before a port leaves the bridge. This is possible since:
https://patchwork.kernel.org/project/netdevbpf/patch/20211024171757.3753288-7-vladimir.oltean@nxp.com/

Additionally, v2 is also rebased over the DSA LAG FDB work.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
31 files changed:
Documentation/networking/dsa/sja1105.rst
drivers/net/dsa/b53/b53_common.c
drivers/net/dsa/b53/b53_priv.h
drivers/net/dsa/dsa_loop.c
drivers/net/dsa/hirschmann/hellcreek.c
drivers/net/dsa/lan9303-core.c
drivers/net/dsa/lantiq_gswip.c
drivers/net/dsa/microchip/ksz9477.c
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h
drivers/net/dsa/mt7530.c
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/ocelot/felix.c
drivers/net/dsa/qca8k.c
drivers/net/dsa/realtek/rtl8366rb.c
drivers/net/dsa/sja1105/sja1105_main.c
drivers/net/dsa/sja1105/sja1105_vl.c
drivers/net/dsa/xrs700x/xrs700x.c
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
include/linux/dsa/8021q.h
include/net/dsa.h
include/soc/mscc/ocelot.h
net/dsa/dsa_priv.h
net/dsa/port.c
net/dsa/switch.c
net/dsa/tag_8021q.c
net/dsa/tag_ocelot_8021q.c
net/dsa/tag_sja1105.c

index 29b1bae..e0219c1 100644 (file)
@@ -293,6 +293,33 @@ of dropped frames, which is a sum of frames dropped due to timing violations,
 lack of destination ports and MTU enforcement checks). Byte-level counters are
 not available.
 
+Limitations
+===========
+
+The SJA1105 switch family always performs VLAN processing. When configured as
+VLAN-unaware, frames carry a different VLAN tag internally, depending on
+whether the port is standalone or under a VLAN-unaware bridge.
+
+The virtual link keys are always fixed at {MAC DA, VLAN ID, VLAN PCP}, but the
+driver asks for the VLAN ID and VLAN PCP when the port is under a VLAN-aware
+bridge. Otherwise, it fills in the VLAN ID and PCP automatically, based on
+whether the port is standalone or in a VLAN-unaware bridge, and accepts only
+"VLAN-unaware" tc-flower keys (MAC DA).
+
+The existing tc-flower keys that are offloaded using virtual links are no
+longer operational after one of the following happens:
+
+- port was standalone and joins a bridge (VLAN-aware or VLAN-unaware)
+- port is part of a bridge whose VLAN awareness state changes
+- port was part of a bridge and becomes standalone
+- port was standalone, but another port joins a VLAN-aware bridge and this
+  changes the global VLAN awareness state of the bridge
+
+The driver cannot veto all these operations, and it cannot update/remove the
+existing tc-flower filters either. So for proper operation, the tc-flower
+filters should be installed only after the forwarding configuration of the port
+has been made, and removed by user space before making any changes to it.
+
 Device Tree bindings and board design
 =====================================
 
index 83bf303..122e637 100644 (file)
@@ -1708,7 +1708,8 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
 }
 
 int b53_fdb_add(struct dsa_switch *ds, int port,
-               const unsigned char *addr, u16 vid)
+               const unsigned char *addr, u16 vid,
+               struct dsa_db db)
 {
        struct b53_device *priv = ds->priv;
        int ret;
@@ -1728,7 +1729,8 @@ int b53_fdb_add(struct dsa_switch *ds, int port,
 EXPORT_SYMBOL(b53_fdb_add);
 
 int b53_fdb_del(struct dsa_switch *ds, int port,
-               const unsigned char *addr, u16 vid)
+               const unsigned char *addr, u16 vid,
+               struct dsa_db db)
 {
        struct b53_device *priv = ds->priv;
        int ret;
@@ -1829,7 +1831,8 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,
 EXPORT_SYMBOL(b53_fdb_dump);
 
 int b53_mdb_add(struct dsa_switch *ds, int port,
-               const struct switchdev_obj_port_mdb *mdb)
+               const struct switchdev_obj_port_mdb *mdb,
+               struct dsa_db db)
 {
        struct b53_device *priv = ds->priv;
        int ret;
@@ -1849,7 +1852,8 @@ int b53_mdb_add(struct dsa_switch *ds, int port,
 EXPORT_SYMBOL(b53_mdb_add);
 
 int b53_mdb_del(struct dsa_switch *ds, int port,
-               const struct switchdev_obj_port_mdb *mdb)
+               const struct switchdev_obj_port_mdb *mdb,
+               struct dsa_db db)
 {
        struct b53_device *priv = ds->priv;
        int ret;
@@ -1865,7 +1869,7 @@ int b53_mdb_del(struct dsa_switch *ds, int port,
 EXPORT_SYMBOL(b53_mdb_del);
 
 int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge,
-               bool *tx_fwd_offload)
+               bool *tx_fwd_offload, struct netlink_ext_ack *extack)
 {
        struct b53_device *dev = ds->priv;
        s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
index a6b339f..86e7eb7 100644 (file)
@@ -324,7 +324,7 @@ void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
 int b53_get_sset_count(struct dsa_switch *ds, int port, int sset);
 void b53_get_ethtool_phy_stats(struct dsa_switch *ds, int port, uint64_t *data);
 int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge,
-               bool *tx_fwd_offload);
+               bool *tx_fwd_offload, struct netlink_ext_ack *extack);
 void b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge);
 void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state);
 void b53_br_fast_age(struct dsa_switch *ds, int port);
@@ -359,15 +359,19 @@ int b53_vlan_add(struct dsa_switch *ds, int port,
 int b53_vlan_del(struct dsa_switch *ds, int port,
                 const struct switchdev_obj_port_vlan *vlan);
 int b53_fdb_add(struct dsa_switch *ds, int port,
-               const unsigned char *addr, u16 vid);
+               const unsigned char *addr, u16 vid,
+               struct dsa_db db);
 int b53_fdb_del(struct dsa_switch *ds, int port,
-               const unsigned char *addr, u16 vid);
+               const unsigned char *addr, u16 vid,
+               struct dsa_db db);
 int b53_fdb_dump(struct dsa_switch *ds, int port,
                 dsa_fdb_dump_cb_t *cb, void *data);
 int b53_mdb_add(struct dsa_switch *ds, int port,
-               const struct switchdev_obj_port_mdb *mdb);
+               const struct switchdev_obj_port_mdb *mdb,
+               struct dsa_db db);
 int b53_mdb_del(struct dsa_switch *ds, int port,
-               const struct switchdev_obj_port_mdb *mdb);
+               const struct switchdev_obj_port_mdb *mdb,
+               struct dsa_db db);
 int b53_mirror_add(struct dsa_switch *ds, int port,
                   struct dsa_mall_mirror_tc_entry *mirror, bool ingress);
 enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port,
index 33daaf1..263e411 100644 (file)
@@ -168,7 +168,8 @@ static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
 
 static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,
                                     struct dsa_bridge bridge,
-                                    bool *tx_fwd_offload)
+                                    bool *tx_fwd_offload,
+                                    struct netlink_ext_ack *extack)
 {
        dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n",
                __func__, port, bridge.dev->name);
index 726f267..ac1f3b3 100644 (file)
@@ -675,7 +675,8 @@ static int hellcreek_bridge_flags(struct dsa_switch *ds, int port,
 
 static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
                                      struct dsa_bridge bridge,
-                                     bool *tx_fwd_offload)
+                                     bool *tx_fwd_offload,
+                                     struct netlink_ext_ack *extack)
 {
        struct hellcreek *hellcreek = ds->priv;
 
@@ -827,7 +828,8 @@ static int hellcreek_fdb_get(struct hellcreek *hellcreek,
 }
 
 static int hellcreek_fdb_add(struct dsa_switch *ds, int port,
-                            const unsigned char *addr, u16 vid)
+                            const unsigned char *addr, u16 vid,
+                            struct dsa_db db)
 {
        struct hellcreek_fdb_entry entry = { 0 };
        struct hellcreek *hellcreek = ds->priv;
@@ -872,7 +874,8 @@ out:
 }
 
 static int hellcreek_fdb_del(struct dsa_switch *ds, int port,
-                            const unsigned char *addr, u16 vid)
+                            const unsigned char *addr, u16 vid,
+                            struct dsa_db db)
 {
        struct hellcreek_fdb_entry entry = { 0 };
        struct hellcreek *hellcreek = ds->priv;
index 3969d89..e03ff1f 100644 (file)
@@ -1111,7 +1111,8 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port)
 
 static int lan9303_port_bridge_join(struct dsa_switch *ds, int port,
                                    struct dsa_bridge bridge,
-                                   bool *tx_fwd_offload)
+                                   bool *tx_fwd_offload,
+                                   struct netlink_ext_ack *extack)
 {
        struct lan9303 *chip = ds->priv;
 
@@ -1188,7 +1189,8 @@ static void lan9303_port_fast_age(struct dsa_switch *ds, int port)
 }
 
 static int lan9303_port_fdb_add(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid)
+                               const unsigned char *addr, u16 vid,
+                               struct dsa_db db)
 {
        struct lan9303 *chip = ds->priv;
 
@@ -1200,8 +1202,8 @@ static int lan9303_port_fdb_add(struct dsa_switch *ds, int port,
 }
 
 static int lan9303_port_fdb_del(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid)
-
+                               const unsigned char *addr, u16 vid,
+                               struct dsa_db db)
 {
        struct lan9303 *chip = ds->priv;
 
@@ -1245,7 +1247,8 @@ static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port,
 }
 
 static int lan9303_port_mdb_add(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb)
+                               const struct switchdev_obj_port_mdb *mdb,
+                               struct dsa_db db)
 {
        struct lan9303 *chip = ds->priv;
        int err;
@@ -1260,7 +1263,8 @@ static int lan9303_port_mdb_add(struct dsa_switch *ds, int port,
 }
 
 static int lan9303_port_mdb_del(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb)
+                               const struct switchdev_obj_port_mdb *mdb,
+                               struct dsa_db db)
 {
        struct lan9303 *chip = ds->priv;
 
index 8a7a809..a8bd233 100644 (file)
@@ -1152,7 +1152,8 @@ static int gswip_vlan_remove(struct gswip_priv *priv,
 
 static int gswip_port_bridge_join(struct dsa_switch *ds, int port,
                                  struct dsa_bridge bridge,
-                                 bool *tx_fwd_offload)
+                                 bool *tx_fwd_offload,
+                                 struct netlink_ext_ack *extack)
 {
        struct net_device *br = bridge.dev;
        struct gswip_priv *priv = ds->priv;
@@ -1389,13 +1390,15 @@ static int gswip_port_fdb(struct dsa_switch *ds, int port,
 }
 
 static int gswip_port_fdb_add(struct dsa_switch *ds, int port,
-                             const unsigned char *addr, u16 vid)
+                             const unsigned char *addr, u16 vid,
+                             struct dsa_db db)
 {
        return gswip_port_fdb(ds, port, addr, vid, true);
 }
 
 static int gswip_port_fdb_del(struct dsa_switch *ds, int port,
-                             const unsigned char *addr, u16 vid)
+                             const unsigned char *addr, u16 vid,
+                             struct dsa_db db)
 {
        return gswip_port_fdb(ds, port, addr, vid, false);
 }
index 18ffc8d..94ad6d9 100644 (file)
@@ -640,7 +640,8 @@ static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port,
 }
 
 static int ksz9477_port_fdb_add(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid)
+                               const unsigned char *addr, u16 vid,
+                               struct dsa_db db)
 {
        struct ksz_device *dev = ds->priv;
        u32 alu_table[4];
@@ -697,7 +698,8 @@ exit:
 }
 
 static int ksz9477_port_fdb_del(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid)
+                               const unsigned char *addr, u16 vid,
+                               struct dsa_db db)
 {
        struct ksz_device *dev = ds->priv;
        u32 alu_table[4];
@@ -839,7 +841,8 @@ exit:
 }
 
 static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb)
+                               const struct switchdev_obj_port_mdb *mdb,
+                               struct dsa_db db)
 {
        struct ksz_device *dev = ds->priv;
        u32 static_table[4];
@@ -914,7 +917,8 @@ exit:
 }
 
 static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb)
+                               const struct switchdev_obj_port_mdb *mdb,
+                               struct dsa_db db)
 {
        struct ksz_device *dev = ds->priv;
        u32 static_table[4];
index 94e618b..8014b18 100644 (file)
@@ -217,7 +217,8 @@ EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats);
 
 int ksz_port_bridge_join(struct dsa_switch *ds, int port,
                         struct dsa_bridge bridge,
-                        bool *tx_fwd_offload)
+                        bool *tx_fwd_offload,
+                        struct netlink_ext_ack *extack)
 {
        /* port_stp_state_set() will be called after to put the port in
         * appropriate state so there is no need to do anything.
@@ -276,7 +277,8 @@ int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
 EXPORT_SYMBOL_GPL(ksz_port_fdb_dump);
 
 int ksz_port_mdb_add(struct dsa_switch *ds, int port,
-                    const struct switchdev_obj_port_mdb *mdb)
+                    const struct switchdev_obj_port_mdb *mdb,
+                    struct dsa_db db)
 {
        struct ksz_device *dev = ds->priv;
        struct alu_struct alu;
@@ -321,7 +323,8 @@ int ksz_port_mdb_add(struct dsa_switch *ds, int port,
 EXPORT_SYMBOL_GPL(ksz_port_mdb_add);
 
 int ksz_port_mdb_del(struct dsa_switch *ds, int port,
-                    const struct switchdev_obj_port_mdb *mdb)
+                    const struct switchdev_obj_port_mdb *mdb,
+                    struct dsa_db db)
 {
        struct ksz_device *dev = ds->priv;
        struct alu_struct alu;
index c6fa487..4ff0a15 100644 (file)
@@ -159,16 +159,19 @@ void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
 int ksz_sset_count(struct dsa_switch *ds, int port, int sset);
 void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf);
 int ksz_port_bridge_join(struct dsa_switch *ds, int port,
-                        struct dsa_bridge bridge, bool *tx_fwd_offload);
+                        struct dsa_bridge bridge, bool *tx_fwd_offload,
+                        struct netlink_ext_ack *extack);
 void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
                           struct dsa_bridge bridge);
 void ksz_port_fast_age(struct dsa_switch *ds, int port);
 int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
                      void *data);
 int ksz_port_mdb_add(struct dsa_switch *ds, int port,
-                    const struct switchdev_obj_port_mdb *mdb);
+                    const struct switchdev_obj_port_mdb *mdb,
+                    struct dsa_db db);
 int ksz_port_mdb_del(struct dsa_switch *ds, int port,
-                    const struct switchdev_obj_port_mdb *mdb);
+                    const struct switchdev_obj_port_mdb *mdb,
+                    struct dsa_db db);
 int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy);
 
 /* Common register access functions */
index f74f25f..66b00c1 100644 (file)
@@ -1186,7 +1186,8 @@ mt7530_port_bridge_flags(struct dsa_switch *ds, int port,
 
 static int
 mt7530_port_bridge_join(struct dsa_switch *ds, int port,
-                       struct dsa_bridge bridge, bool *tx_fwd_offload)
+                       struct dsa_bridge bridge, bool *tx_fwd_offload,
+                       struct netlink_ext_ack *extack)
 {
        struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
        u32 port_bitmap = BIT(MT7530_CPU_PORT);
@@ -1349,7 +1350,8 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
 
 static int
 mt7530_port_fdb_add(struct dsa_switch *ds, int port,
-                   const unsigned char *addr, u16 vid)
+                   const unsigned char *addr, u16 vid,
+                   struct dsa_db db)
 {
        struct mt7530_priv *priv = ds->priv;
        int ret;
@@ -1365,7 +1367,8 @@ mt7530_port_fdb_add(struct dsa_switch *ds, int port,
 
 static int
 mt7530_port_fdb_del(struct dsa_switch *ds, int port,
-                   const unsigned char *addr, u16 vid)
+                   const unsigned char *addr, u16 vid,
+                   struct dsa_db db)
 {
        struct mt7530_priv *priv = ds->priv;
        int ret;
@@ -1416,7 +1419,8 @@ err:
 
 static int
 mt7530_port_mdb_add(struct dsa_switch *ds, int port,
-                   const struct switchdev_obj_port_mdb *mdb)
+                   const struct switchdev_obj_port_mdb *mdb,
+                   struct dsa_db db)
 {
        struct mt7530_priv *priv = ds->priv;
        const u8 *addr = mdb->addr;
@@ -1442,7 +1446,8 @@ mt7530_port_mdb_add(struct dsa_switch *ds, int port,
 
 static int
 mt7530_port_mdb_del(struct dsa_switch *ds, int port,
-                   const struct switchdev_obj_port_mdb *mdb)
+                   const struct switchdev_obj_port_mdb *mdb,
+                   struct dsa_db db)
 {
        struct mt7530_priv *priv = ds->priv;
        const u8 *addr = mdb->addr;
index 1b9a20b..84b90fc 100644 (file)
@@ -2456,7 +2456,8 @@ unlock:
 }
 
 static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
-                                 const unsigned char *addr, u16 vid)
+                                 const unsigned char *addr, u16 vid,
+                                 struct dsa_db db)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
@@ -2470,7 +2471,8 @@ static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
 }
 
 static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
-                                 const unsigned char *addr, u16 vid)
+                                 const unsigned char *addr, u16 vid,
+                                 struct dsa_db db)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
@@ -2616,7 +2618,8 @@ static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds,
 
 static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
                                      struct dsa_bridge bridge,
-                                     bool *tx_fwd_offload)
+                                     bool *tx_fwd_offload,
+                                     struct netlink_ext_ack *extack)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
@@ -2682,7 +2685,8 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
 
 static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,
                                           int tree_index, int sw_index,
-                                          int port, struct dsa_bridge bridge)
+                                          int port, struct dsa_bridge bridge,
+                                          struct netlink_ext_ack *extack)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
@@ -6002,7 +6006,8 @@ static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port,
 }
 
 static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
-                                 const struct switchdev_obj_port_mdb *mdb)
+                                 const struct switchdev_obj_port_mdb *mdb,
+                                 struct dsa_db db)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
@@ -6016,7 +6021,8 @@ static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
 }
 
 static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
-                                 const struct switchdev_obj_port_mdb *mdb)
+                                 const struct switchdev_obj_port_mdb *mdb,
+                                 struct dsa_db db)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
index 1d7c5d7..5fc740e 100644 (file)
 #include <net/dsa.h>
 #include "felix.h"
 
-static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid,
-                                     bool pvid, bool untagged)
+/* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that
+ * the tagger can perform RX source port identification.
+ */
+static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid)
 {
        struct ocelot_vcap_filter *outer_tagging_rule;
        struct ocelot *ocelot = &felix->ocelot;
        struct dsa_switch *ds = felix->ds;
        int key_length, upstream, err;
 
-       /* We don't need to install the rxvlan into the other ports' filtering
-        * tables, because we're just pushing the rxvlan when sending towards
-        * the CPU
-        */
-       if (!pvid)
-               return 0;
-
        key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length;
        upstream = dsa_upstream_port(ds, port);
 
@@ -71,21 +66,32 @@ static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid,
        return err;
 }
 
-static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid,
-                                     bool pvid, bool untagged)
+static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid)
+{
+       struct ocelot_vcap_filter *outer_tagging_rule;
+       struct ocelot_vcap_block *block_vcap_es0;
+       struct ocelot *ocelot = &felix->ocelot;
+
+       block_vcap_es0 = &ocelot->block[VCAP_ES0];
+
+       outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0,
+                                                                port, false);
+       if (!outer_tagging_rule)
+               return -ENOENT;
+
+       return ocelot_vcap_filter_del(ocelot, outer_tagging_rule);
+}
+
+/* Set up VCAP IS1 rules for stripping the tag_8021q VLAN on TX and VCAP IS2
+ * rules for steering those tagged packets towards the correct destination port
+ */
+static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
 {
        struct ocelot_vcap_filter *untagging_rule, *redirect_rule;
        struct ocelot *ocelot = &felix->ocelot;
        struct dsa_switch *ds = felix->ds;
        int upstream, err;
 
-       /* tag_8021q.c assumes we are implementing this via port VLAN
-        * membership, which we aren't. So we don't need to add any VCAP filter
-        * for the CPU port.
-        */
-       if (ocelot->ports[port]->is_dsa_8021q_cpu)
-               return 0;
-
        untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
        if (!untagging_rule)
                return -ENOMEM;
@@ -142,49 +148,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid,
        return 0;
 }
 
-static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
-                                   u16 flags)
-{
-       bool untagged = flags & BRIDGE_VLAN_INFO_UNTAGGED;
-       bool pvid = flags & BRIDGE_VLAN_INFO_PVID;
-       struct ocelot *ocelot = ds->priv;
-
-       if (vid_is_dsa_8021q_rxvlan(vid))
-               return felix_tag_8021q_rxvlan_add(ocelot_to_felix(ocelot),
-                                                 port, vid, pvid, untagged);
-
-       if (vid_is_dsa_8021q_txvlan(vid))
-               return felix_tag_8021q_txvlan_add(ocelot_to_felix(ocelot),
-                                                 port, vid, pvid, untagged);
-
-       return 0;
-}
-
-static int felix_tag_8021q_rxvlan_del(struct felix *felix, int port, u16 vid)
-{
-       struct ocelot_vcap_filter *outer_tagging_rule;
-       struct ocelot_vcap_block *block_vcap_es0;
-       struct ocelot *ocelot = &felix->ocelot;
-
-       block_vcap_es0 = &ocelot->block[VCAP_ES0];
-
-       outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0,
-                                                                port, false);
-       /* In rxvlan_add, we had the "if (!pvid) return 0" logic to avoid
-        * installing outer tagging ES0 rules where they weren't needed.
-        * But in rxvlan_del, the API doesn't give us the "flags" anymore,
-        * so that forces us to be slightly sloppy here, and just assume that
-        * if we didn't find an outer_tagging_rule it means that there was
-        * none in the first place, i.e. rxvlan_del is called on a non-pvid
-        * port. This is most probably true though.
-        */
-       if (!outer_tagging_rule)
-               return 0;
-
-       return ocelot_vcap_filter_del(ocelot, outer_tagging_rule);
-}
-
-static int felix_tag_8021q_txvlan_del(struct felix *felix, int port, u16 vid)
+static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid)
 {
        struct ocelot_vcap_filter *untagging_rule, *redirect_rule;
        struct ocelot_vcap_block *block_vcap_is1;
@@ -192,16 +156,13 @@ static int felix_tag_8021q_txvlan_del(struct felix *felix, int port, u16 vid)
        struct ocelot *ocelot = &felix->ocelot;
        int err;
 
-       if (ocelot->ports[port]->is_dsa_8021q_cpu)
-               return 0;
-
        block_vcap_is1 = &ocelot->block[VCAP_IS1];
        block_vcap_is2 = &ocelot->block[VCAP_IS2];
 
        untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1,
                                                             port, false);
        if (!untagging_rule)
-               return 0;
+               return -ENOENT;
 
        err = ocelot_vcap_filter_del(ocelot, untagging_rule);
        if (err)
@@ -210,22 +171,54 @@ static int felix_tag_8021q_txvlan_del(struct felix *felix, int port, u16 vid)
        redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2,
                                                            port, false);
        if (!redirect_rule)
-               return 0;
+               return -ENOENT;
 
        return ocelot_vcap_filter_del(ocelot, redirect_rule);
 }
 
+static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
+                                   u16 flags)
+{
+       struct ocelot *ocelot = ds->priv;
+       int err;
+
+       /* tag_8021q.c assumes we are implementing this via port VLAN
+        * membership, which we aren't. So we don't need to add any VCAP filter
+        * for the CPU port.
+        */
+       if (!dsa_is_user_port(ds, port))
+               return 0;
+
+       err = felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid);
+       if (err)
+               return err;
+
+       err = felix_tag_8021q_vlan_add_tx(ocelot_to_felix(ocelot), port, vid);
+       if (err) {
+               felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid);
+               return err;
+       }
+
+       return 0;
+}
+
 static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
 {
        struct ocelot *ocelot = ds->priv;
+       int err;
 
-       if (vid_is_dsa_8021q_rxvlan(vid))
-               return felix_tag_8021q_rxvlan_del(ocelot_to_felix(ocelot),
-                                                 port, vid);
+       if (!dsa_is_user_port(ds, port))
+               return 0;
 
-       if (vid_is_dsa_8021q_txvlan(vid))
-               return felix_tag_8021q_txvlan_del(ocelot_to_felix(ocelot),
-                                                 port, vid);
+       err = felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid);
+       if (err)
+               return err;
+
+       err = felix_tag_8021q_vlan_del_tx(ocelot_to_felix(ocelot), port, vid);
+       if (err) {
+               felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid);
+               return err;
+       }
 
        return 0;
 }
@@ -241,7 +234,7 @@ static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port)
 {
        mutex_lock(&ocelot->fwd_domain_lock);
 
-       ocelot->ports[port]->is_dsa_8021q_cpu = true;
+       ocelot_port_set_dsa_8021q_cpu(ocelot, port);
        ocelot->npi = -1;
 
        /* Overwrite PGID_CPU with the non-tagging port */
@@ -257,6 +250,7 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)
        mutex_lock(&ocelot->fwd_domain_lock);
 
        ocelot->ports[port]->is_dsa_8021q_cpu = false;
+       ocelot_port_unset_dsa_8021q_cpu(ocelot, port);
 
        /* Restore PGID_CPU */
        ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID,
@@ -598,52 +592,99 @@ static int felix_fdb_dump(struct dsa_switch *ds, int port,
        return ocelot_fdb_dump(ocelot, port, cb, data);
 }
 
+/* Translate the DSA database API into the ocelot switch library API,
+ * which uses VID 0 for all ports that aren't part of a bridge,
+ * and expects the bridge_dev to be NULL in that case.
+ */
+static struct net_device *felix_classify_db(struct dsa_db db)
+{
+       switch (db.type) {
+       case DSA_DB_PORT:
+       case DSA_DB_LAG:
+               return NULL;
+       case DSA_DB_BRIDGE:
+               return db.bridge.dev;
+       default:
+               return ERR_PTR(-EOPNOTSUPP);
+       }
+}
+
 static int felix_fdb_add(struct dsa_switch *ds, int port,
-                        const unsigned char *addr, u16 vid)
+                        const unsigned char *addr, u16 vid,
+                        struct dsa_db db)
 {
+       struct net_device *bridge_dev = felix_classify_db(db);
        struct ocelot *ocelot = ds->priv;
 
-       return ocelot_fdb_add(ocelot, port, addr, vid);
+       if (IS_ERR(bridge_dev))
+               return PTR_ERR(bridge_dev);
+
+       return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev);
 }
 
 static int felix_fdb_del(struct dsa_switch *ds, int port,
-                        const unsigned char *addr, u16 vid)
+                        const unsigned char *addr, u16 vid,
+                        struct dsa_db db)
 {
+       struct net_device *bridge_dev = felix_classify_db(db);
        struct ocelot *ocelot = ds->priv;
 
-       return ocelot_fdb_del(ocelot, port, addr, vid);
+       if (IS_ERR(bridge_dev))
+               return PTR_ERR(bridge_dev);
+
+       return ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev);
 }
 
 static int felix_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
-                            const unsigned char *addr, u16 vid)
+                            const unsigned char *addr, u16 vid,
+                            struct dsa_db db)
 {
+       struct net_device *bridge_dev = felix_classify_db(db);
        struct ocelot *ocelot = ds->priv;
 
-       return ocelot_lag_fdb_add(ocelot, lag.dev, addr, vid);
+       if (IS_ERR(bridge_dev))
+               return PTR_ERR(bridge_dev);
+
+       return ocelot_lag_fdb_add(ocelot, lag.dev, addr, vid, bridge_dev);
 }
 
 static int felix_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
-                            const unsigned char *addr, u16 vid)
+                            const unsigned char *addr, u16 vid,
+                            struct dsa_db db)
 {
+       struct net_device *bridge_dev = felix_classify_db(db);
        struct ocelot *ocelot = ds->priv;
 
-       return ocelot_lag_fdb_del(ocelot, lag.dev, addr, vid);
+       if (IS_ERR(bridge_dev))
+               return PTR_ERR(bridge_dev);
+
+       return ocelot_lag_fdb_del(ocelot, lag.dev, addr, vid, bridge_dev);
 }
 
 static int felix_mdb_add(struct dsa_switch *ds, int port,
-                        const struct switchdev_obj_port_mdb *mdb)
+                        const struct switchdev_obj_port_mdb *mdb,
+                        struct dsa_db db)
 {
+       struct net_device *bridge_dev = felix_classify_db(db);
        struct ocelot *ocelot = ds->priv;
 
-       return ocelot_port_mdb_add(ocelot, port, mdb);
+       if (IS_ERR(bridge_dev))
+               return PTR_ERR(bridge_dev);
+
+       return ocelot_port_mdb_add(ocelot, port, mdb, bridge_dev);
 }
 
 static int felix_mdb_del(struct dsa_switch *ds, int port,
-                        const struct switchdev_obj_port_mdb *mdb)
+                        const struct switchdev_obj_port_mdb *mdb,
+                        struct dsa_db db)
 {
+       struct net_device *bridge_dev = felix_classify_db(db);
        struct ocelot *ocelot = ds->priv;
 
-       return ocelot_port_mdb_del(ocelot, port, mdb);
+       if (IS_ERR(bridge_dev))
+               return PTR_ERR(bridge_dev);
+
+       return ocelot_port_mdb_del(ocelot, port, mdb, bridge_dev);
 }
 
 static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port,
@@ -675,13 +716,13 @@ static int felix_bridge_flags(struct dsa_switch *ds, int port,
 }
 
 static int felix_bridge_join(struct dsa_switch *ds, int port,
-                            struct dsa_bridge bridge, bool *tx_fwd_offload)
+                            struct dsa_bridge bridge, bool *tx_fwd_offload,
+                            struct netlink_ext_ack *extack)
 {
        struct ocelot *ocelot = ds->priv;
 
-       ocelot_port_bridge_join(ocelot, port, bridge.dev);
-
-       return 0;
+       return ocelot_port_bridge_join(ocelot, port, bridge.dev, bridge.num,
+                                      extack);
 }
 
 static void felix_bridge_leave(struct dsa_switch *ds, int port,
@@ -1208,6 +1249,8 @@ static int felix_setup(struct dsa_switch *ds)
 
        ds->mtu_enforcement_ingress = true;
        ds->assisted_learning_on_cpu_port = true;
+       ds->fdb_isolation = true;
+       ds->max_num_bridges = ds->num_ports;
 
        return 0;
 
index 990ed3b..ee0dbf3 100644 (file)
@@ -2247,7 +2247,8 @@ qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
 
 static int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
                                  struct dsa_bridge bridge,
-                                 bool *tx_fwd_offload)
+                                 bool *tx_fwd_offload,
+                                 struct netlink_ext_ack *extack)
 {
        struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
        int port_mask, cpu_port;
@@ -2398,7 +2399,8 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
 
 static int
 qca8k_port_fdb_add(struct dsa_switch *ds, int port,
-                  const unsigned char *addr, u16 vid)
+                  const unsigned char *addr, u16 vid,
+                  struct dsa_db db)
 {
        struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
        u16 port_mask = BIT(port);
@@ -2408,7 +2410,8 @@ qca8k_port_fdb_add(struct dsa_switch *ds, int port,
 
 static int
 qca8k_port_fdb_del(struct dsa_switch *ds, int port,
-                  const unsigned char *addr, u16 vid)
+                  const unsigned char *addr, u16 vid,
+                  struct dsa_db db)
 {
        struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
        u16 port_mask = BIT(port);
@@ -2445,7 +2448,8 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
 
 static int
 qca8k_port_mdb_add(struct dsa_switch *ds, int port,
-                  const struct switchdev_obj_port_mdb *mdb)
+                  const struct switchdev_obj_port_mdb *mdb,
+                  struct dsa_db db)
 {
        struct qca8k_priv *priv = ds->priv;
        const u8 *addr = mdb->addr;
@@ -2456,7 +2460,8 @@ qca8k_port_mdb_add(struct dsa_switch *ds, int port,
 
 static int
 qca8k_port_mdb_del(struct dsa_switch *ds, int port,
-                  const struct switchdev_obj_port_mdb *mdb)
+                  const struct switchdev_obj_port_mdb *mdb,
+                  struct dsa_db db)
 {
        struct qca8k_priv *priv = ds->priv;
        const u8 *addr = mdb->addr;
index fb6565e..1a3406b 100644 (file)
@@ -1189,7 +1189,8 @@ rtl8366rb_port_disable(struct dsa_switch *ds, int port)
 static int
 rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,
                           struct dsa_bridge bridge,
-                          bool *tx_fwd_offload)
+                          bool *tx_fwd_offload,
+                          struct netlink_ext_ack *extack)
 {
        struct realtek_priv *priv = ds->priv;
        unsigned int port_bitmap = 0;
index 8fc3094..380f078 100644 (file)
@@ -393,10 +393,8 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
                .start_dynspc = 0,
                /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */
                .poly = 0x97,
-               /* This selects between Independent VLAN Learning (IVL) and
-                * Shared VLAN Learning (SVL)
-                */
-               .shared_learn = true,
+               /* Always use Independent VLAN Learning (IVL) */
+               .shared_learn = false,
                /* Don't discard management traffic based on ENFPORT -
                 * we don't perform SMAC port enforcement anyway, so
                 * what we are setting here doesn't matter.
@@ -1803,25 +1801,52 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
 }
 
 static int sja1105_fdb_add(struct dsa_switch *ds, int port,
-                          const unsigned char *addr, u16 vid)
+                          const unsigned char *addr, u16 vid,
+                          struct dsa_db db)
 {
        struct sja1105_private *priv = ds->priv;
 
+       if (!vid) {
+               switch (db.type) {
+               case DSA_DB_PORT:
+                       vid = dsa_tag_8021q_standalone_vid(db.dp);
+                       break;
+               case DSA_DB_BRIDGE:
+                       vid = dsa_tag_8021q_bridge_vid(db.bridge.num);
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       }
+
        return priv->info->fdb_add_cmd(ds, port, addr, vid);
 }
 
 static int sja1105_fdb_del(struct dsa_switch *ds, int port,
-                          const unsigned char *addr, u16 vid)
+                          const unsigned char *addr, u16 vid,
+                          struct dsa_db db)
 {
        struct sja1105_private *priv = ds->priv;
 
+       if (!vid) {
+               switch (db.type) {
+               case DSA_DB_PORT:
+                       vid = dsa_tag_8021q_standalone_vid(db.dp);
+                       break;
+               case DSA_DB_BRIDGE:
+                       vid = dsa_tag_8021q_bridge_vid(db.bridge.num);
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       }
+
        return priv->info->fdb_del_cmd(ds, port, addr, vid);
 }
 
 static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
                            dsa_fdb_dump_cb_t *cb, void *data)
 {
-       struct dsa_port *dp = dsa_to_port(ds, port);
        struct sja1105_private *priv = ds->priv;
        struct device *dev = ds->dev;
        int i;
@@ -1858,7 +1883,7 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
                u64_to_ether_addr(l2_lookup.macaddr, macaddr);
 
                /* We need to hide the dsa_8021q VLANs from the user. */
-               if (!dsa_port_is_vlan_filtering(dp))
+               if (vid_is_dsa_8021q(l2_lookup.vlanid))
                        l2_lookup.vlanid = 0;
                rc = cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data);
                if (rc)
@@ -1869,7 +1894,15 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
 
 static void sja1105_fast_age(struct dsa_switch *ds, int port)
 {
+       struct dsa_port *dp = dsa_to_port(ds, port);
        struct sja1105_private *priv = ds->priv;
+       struct dsa_db db = {
+               .type = DSA_DB_BRIDGE,
+               .bridge = {
+                       .dev = dsa_port_bridge_dev_get(dp),
+                       .num = dsa_port_bridge_num_get(dp),
+               },
+       };
        int i;
 
        for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) {
@@ -1897,7 +1930,7 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port)
 
                u64_to_ether_addr(l2_lookup.macaddr, macaddr);
 
-               rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid);
+               rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db);
                if (rc) {
                        dev_err(ds->dev,
                                "Failed to delete FDB entry %pM vid %lld: %pe\n",
@@ -1908,15 +1941,17 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port)
 }
 
 static int sja1105_mdb_add(struct dsa_switch *ds, int port,
-                          const struct switchdev_obj_port_mdb *mdb)
+                          const struct switchdev_obj_port_mdb *mdb,
+                          struct dsa_db db)
 {
-       return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid);
+       return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid, db);
 }
 
 static int sja1105_mdb_del(struct dsa_switch *ds, int port,
-                          const struct switchdev_obj_port_mdb *mdb)
+                          const struct switchdev_obj_port_mdb *mdb,
+                          struct dsa_db db)
 {
-       return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid);
+       return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid, db);
 }
 
 /* Common function for unicast and broadcast flood configuration.
@@ -2059,7 +2094,8 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,
 
 static int sja1105_bridge_join(struct dsa_switch *ds, int port,
                               struct dsa_bridge bridge,
-                              bool *tx_fwd_offload)
+                              bool *tx_fwd_offload,
+                              struct netlink_ext_ack *extack)
 {
        int rc;
 
@@ -2067,7 +2103,7 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port,
        if (rc)
                return rc;
 
-       rc = dsa_tag_8021q_bridge_tx_fwd_offload(ds, port, bridge);
+       rc = dsa_tag_8021q_bridge_join(ds, port, bridge);
        if (rc) {
                sja1105_bridge_member(ds, port, bridge, false);
                return rc;
@@ -2081,7 +2117,7 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port,
 static void sja1105_bridge_leave(struct dsa_switch *ds, int port,
                                 struct dsa_bridge bridge)
 {
-       dsa_tag_8021q_bridge_tx_fwd_unoffload(ds, port, bridge);
+       dsa_tag_8021q_bridge_leave(ds, port, bridge);
        sja1105_bridge_member(ds, port, bridge, false);
 }
 
@@ -2341,7 +2377,6 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port,
 int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
                           struct netlink_ext_ack *extack)
 {
-       struct sja1105_l2_lookup_params_entry *l2_lookup_params;
        struct sja1105_general_params_entry *general_params;
        struct sja1105_private *priv = ds->priv;
        struct sja1105_table *table;
@@ -2379,28 +2414,6 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
        general_params->incl_srcpt1 = enabled;
        general_params->incl_srcpt0 = enabled;
 
-       /* VLAN filtering => independent VLAN learning.
-        * No VLAN filtering (or best effort) => shared VLAN learning.
-        *
-        * In shared VLAN learning mode, untagged traffic still gets
-        * pvid-tagged, and the FDB table gets populated with entries
-        * containing the "real" (pvid or from VLAN tag) VLAN ID.
-        * However the switch performs a masked L2 lookup in the FDB,
-        * effectively only looking up a frame's DMAC (and not VID) for the
-        * forwarding decision.
-        *
-        * This is extremely convenient for us, because in modes with
-        * vlan_filtering=0, dsa_8021q actually installs unique pvid's into
-        * each front panel port. This is good for identification but breaks
-        * learning badly - the VID of the learnt FDB entry is unique, aka
-        * no frames coming from any other port are going to have it. So
-        * for forwarding purposes, this is as though learning was broken
-        * (all frames get flooded).
-        */
-       table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS];
-       l2_lookup_params = table->entries;
-       l2_lookup_params->shared_learn = !enabled;
-
        for (port = 0; port < ds->num_ports; port++) {
                if (dsa_is_unused_port(ds, port))
                        continue;
@@ -2509,7 +2522,7 @@ static int sja1105_bridge_vlan_add(struct dsa_switch *ds, int port,
         */
        if (vid_is_dsa_8021q(vlan->vid)) {
                NL_SET_ERR_MSG_MOD(extack,
-                                  "Range 1024-3071 reserved for dsa_8021q operation");
+                                  "Range 3072-4095 reserved for dsa_8021q operation");
                return -EBUSY;
        }
 
@@ -3086,6 +3099,7 @@ static int sja1105_setup(struct dsa_switch *ds)
         */
        ds->vlan_filtering_is_global = true;
        ds->untag_bridge_pvid = true;
+       ds->fdb_isolation = true;
        /* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */
        ds->max_num_bridges = 7;
 
index f5dca6a..b7e95d6 100644 (file)
@@ -296,6 +296,19 @@ static bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a,
        return false;
 }
 
+/* FIXME: this should change when the bridge upper of the port changes. */
+static u16 sja1105_port_get_tag_8021q_vid(struct dsa_port *dp)
+{
+       unsigned long bridge_num;
+
+       if (!dp->bridge)
+               return dsa_tag_8021q_standalone_vid(dp);
+
+       bridge_num = dsa_port_bridge_num_get(dp);
+
+       return dsa_tag_8021q_bridge_vid(bridge_num);
+}
+
 static int sja1105_init_virtual_links(struct sja1105_private *priv,
                                      struct netlink_ext_ack *extack)
 {
@@ -394,8 +407,9 @@ static int sja1105_init_virtual_links(struct sja1105_private *priv,
                                vl_lookup[k].vlanid = rule->key.vl.vid;
                                vl_lookup[k].vlanprior = rule->key.vl.pcp;
                        } else {
+                               /* FIXME */
                                struct dsa_port *dp = dsa_to_port(priv->ds, port);
-                               u16 vid = dsa_tag_8021q_rx_vid(dp);
+                               u16 vid = sja1105_port_get_tag_8021q_vid(dp);
 
                                vl_lookup[k].vlanid = vid;
                                vl_lookup[k].vlanprior = 0;
index bc06fe6..3887ed3 100644 (file)
@@ -534,7 +534,8 @@ static int xrs700x_bridge_common(struct dsa_switch *ds, int port,
 }
 
 static int xrs700x_bridge_join(struct dsa_switch *ds, int port,
-                              struct dsa_bridge bridge, bool *tx_fwd_offload)
+                              struct dsa_bridge bridge, bool *tx_fwd_offload,
+                              struct netlink_ext_ack *extack)
 {
        return xrs700x_bridge_common(ds, port, bridge, true);
 }
index 0e8fa0a..0af321f 100644 (file)
@@ -13,6 +13,7 @@
 
 #define TABLE_UPDATE_SLEEP_US 10
 #define TABLE_UPDATE_TIMEOUT_US 100000
+#define OCELOT_RSV_VLAN_RANGE_START 4000
 
 struct ocelot_mact_entry {
        u8 mac[ETH_ALEN];
@@ -221,6 +222,35 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int port)
                       REW_PORT_CFG, port);
 }
 
+static int ocelot_single_vlan_aware_bridge(struct ocelot *ocelot,
+                                          struct netlink_ext_ack *extack)
+{
+       struct net_device *bridge = NULL;
+       int port;
+
+       for (port = 0; port < ocelot->num_phys_ports; port++) {
+               struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+               if (!ocelot_port || !ocelot_port->bridge ||
+                   !br_vlan_enabled(ocelot_port->bridge))
+                       continue;
+
+               if (!bridge) {
+                       bridge = ocelot_port->bridge;
+                       continue;
+               }
+
+               if (bridge == ocelot_port->bridge)
+                       continue;
+
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Only one VLAN-aware bridge is supported");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
 static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
 {
        return ocelot_read(ocelot, ANA_TABLES_VLANACCESS);
@@ -347,12 +377,45 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port)
        }
 }
 
+int ocelot_bridge_num_find(struct ocelot *ocelot,
+                          const struct net_device *bridge)
+{
+       int port;
+
+       for (port = 0; port < ocelot->num_phys_ports; port++) {
+               struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+               if (ocelot_port && ocelot_port->bridge == bridge)
+                       return ocelot_port->bridge_num;
+       }
+
+       return -1;
+}
+EXPORT_SYMBOL_GPL(ocelot_bridge_num_find);
+
+static u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot,
+                                   const struct net_device *bridge)
+{
+       int bridge_num;
+
+       /* Standalone ports use VID 0 */
+       if (!bridge)
+               return 0;
+
+       bridge_num = ocelot_bridge_num_find(ocelot, bridge);
+       if (WARN_ON(bridge_num < 0))
+               return 0;
+
+       /* VLAN-unaware bridges use a reserved VID going from 4095 downwards */
+       return VLAN_N_VID - bridge_num - 1;
+}
+
 /* Default vlan to clasify for untagged frames (may be zero) */
 static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
                                 const struct ocelot_bridge_vlan *pvid_vlan)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
-       u16 pvid = OCELOT_VLAN_UNAWARE_PVID;
+       u16 pvid = ocelot_vlan_unaware_pvid(ocelot, ocelot_port->bridge);
        u32 val = 0;
 
        ocelot_port->pvid_vlan = pvid_vlan;
@@ -466,12 +529,29 @@ static int ocelot_vlan_member_del(struct ocelot *ocelot, int port, u16 vid)
        return 0;
 }
 
+static int ocelot_add_vlan_unaware_pvid(struct ocelot *ocelot, int port,
+                                       const struct net_device *bridge)
+{
+       u16 vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
+       return ocelot_vlan_member_add(ocelot, port, vid, true);
+}
+
+static int ocelot_del_vlan_unaware_pvid(struct ocelot *ocelot, int port,
+                                       const struct net_device *bridge)
+{
+       u16 vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
+       return ocelot_vlan_member_del(ocelot, port, vid);
+}
+
 int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
                               bool vlan_aware, struct netlink_ext_ack *extack)
 {
        struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1];
        struct ocelot_port *ocelot_port = ocelot->ports[port];
        struct ocelot_vcap_filter *filter;
+       int err;
        u32 val;
 
        list_for_each_entry(filter, &block->rules, list) {
@@ -483,6 +563,19 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
                }
        }
 
+       err = ocelot_single_vlan_aware_bridge(ocelot, extack);
+       if (err)
+               return err;
+
+       if (vlan_aware)
+               err = ocelot_del_vlan_unaware_pvid(ocelot, port,
+                                                  ocelot_port->bridge);
+       else
+               err = ocelot_add_vlan_unaware_pvid(ocelot, port,
+                                                  ocelot_port->bridge);
+       if (err)
+               return err;
+
        ocelot_port->vlan_aware = vlan_aware;
 
        if (vlan_aware)
@@ -521,6 +614,12 @@ int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
                }
        }
 
+       if (vid > OCELOT_RSV_VLAN_RANGE_START) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "VLAN range 4000-4095 reserved for VLAN-unaware bridging");
+               return -EBUSY;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(ocelot_vlan_prepare);
@@ -584,11 +683,11 @@ static void ocelot_vlan_init(struct ocelot *ocelot)
        for (vid = 1; vid < VLAN_N_VID; vid++)
                ocelot_vlant_set_mask(ocelot, vid, 0);
 
-       /* Because VLAN filtering is enabled, we need VID 0 to get untagged
-        * traffic.  It is added automatically if 8021q module is loaded, but
-        * we can't rely on it since module may be not loaded.
+       /* We need VID 0 to get traffic on standalone ports.
+        * It is added automatically if the 8021q module is loaded, but we
+        * can't rely on that since it might not be.
         */
-       ocelot_vlant_set_mask(ocelot, OCELOT_VLAN_UNAWARE_PVID, all_ports);
+       ocelot_vlant_set_mask(ocelot, OCELOT_STANDALONE_PVID, all_ports);
 
        /* Set vlan ingress filter mask to all ports but the CPU port by
         * default.
@@ -1237,21 +1336,27 @@ void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
 }
 EXPORT_SYMBOL(ocelot_drain_cpu_queue);
 
-int ocelot_fdb_add(struct ocelot *ocelot, int port,
-                  const unsigned char *addr, u16 vid)
+int ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr,
+                  u16 vid, const struct net_device *bridge)
 {
        int pgid = port;
 
        if (port == ocelot->npi)
                pgid = PGID_CPU;
 
+       if (!vid)
+               vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
        return ocelot_mact_learn(ocelot, pgid, addr, vid, ENTRYTYPE_LOCKED);
 }
 EXPORT_SYMBOL(ocelot_fdb_add);
 
-int ocelot_fdb_del(struct ocelot *ocelot, int port,
-                  const unsigned char *addr, u16 vid)
+int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr,
+                  u16 vid, const struct net_device *bridge)
 {
+       if (!vid)
+               vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
        return ocelot_mact_forget(ocelot, addr, vid);
 }
 EXPORT_SYMBOL(ocelot_fdb_del);
@@ -1413,6 +1518,12 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
 
                        is_static = (entry.type == ENTRYTYPE_LOCKED);
 
+                       /* Hide the reserved VLANs used for
+                        * VLAN-unaware bridging.
+                        */
+                       if (entry.vid > OCELOT_RSV_VLAN_RANGE_START)
+                               entry.vid = 0;
+
                        err = cb(entry.mac, entry.vid, is_static, data);
                        if (err)
                                break;
@@ -2054,6 +2165,28 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
 }
 EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);
 
+void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port)
+{
+       u16 vid;
+
+       ocelot->ports[port]->is_dsa_8021q_cpu = true;
+
+       for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++)
+               ocelot_vlan_member_add(ocelot, port, vid, true);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_set_dsa_8021q_cpu);
+
+void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port)
+{
+       u16 vid;
+
+       ocelot->ports[port]->is_dsa_8021q_cpu = false;
+
+       for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++)
+               ocelot_vlan_member_del(ocelot, port, vid);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_unset_dsa_8021q_cpu);
+
 void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
@@ -2198,7 +2331,8 @@ static void ocelot_encode_ports_to_mdb(unsigned char *addr,
 }
 
 int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
-                       const struct switchdev_obj_port_mdb *mdb)
+                       const struct switchdev_obj_port_mdb *mdb,
+                       const struct net_device *bridge)
 {
        unsigned char addr[ETH_ALEN];
        struct ocelot_multicast *mc;
@@ -2208,6 +2342,9 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
        if (port == ocelot->npi)
                port = ocelot->num_phys_ports;
 
+       if (!vid)
+               vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
        mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
        if (!mc) {
                /* New entry */
@@ -2254,7 +2391,8 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
 EXPORT_SYMBOL(ocelot_port_mdb_add);
 
 int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
-                       const struct switchdev_obj_port_mdb *mdb)
+                       const struct switchdev_obj_port_mdb *mdb,
+                       const struct net_device *bridge)
 {
        unsigned char addr[ETH_ALEN];
        struct ocelot_multicast *mc;
@@ -2264,6 +2402,9 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
        if (port == ocelot->npi)
                port = ocelot->num_phys_ports;
 
+       if (!vid)
+               vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
        mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
        if (!mc)
                return -ENOENT;
@@ -2297,18 +2438,30 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
 }
 EXPORT_SYMBOL(ocelot_port_mdb_del);
 
-void ocelot_port_bridge_join(struct ocelot *ocelot, int port,
-                            struct net_device *bridge)
+int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
+                           struct net_device *bridge, int bridge_num,
+                           struct netlink_ext_ack *extack)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
+       int err;
+
+       err = ocelot_single_vlan_aware_bridge(ocelot, extack);
+       if (err)
+               return err;
 
        mutex_lock(&ocelot->fwd_domain_lock);
 
        ocelot_port->bridge = bridge;
+       ocelot_port->bridge_num = bridge_num;
 
        ocelot_apply_bridge_fwd_mask(ocelot, true);
 
        mutex_unlock(&ocelot->fwd_domain_lock);
+
+       if (br_vlan_enabled(bridge))
+               return 0;
+
+       return ocelot_add_vlan_unaware_pvid(ocelot, port, bridge);
 }
 EXPORT_SYMBOL(ocelot_port_bridge_join);
 
@@ -2319,7 +2472,11 @@ void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
 
        mutex_lock(&ocelot->fwd_domain_lock);
 
+       if (!br_vlan_enabled(bridge))
+               ocelot_del_vlan_unaware_pvid(ocelot, port, bridge);
+
        ocelot_port->bridge = NULL;
+       ocelot_port->bridge_num = -1;
 
        ocelot_port_set_pvid(ocelot, port, NULL);
        ocelot_port_manage_port_tag(ocelot, port);
@@ -2544,7 +2701,8 @@ void ocelot_port_lag_change(struct ocelot *ocelot, int port, bool lag_tx_active)
 EXPORT_SYMBOL(ocelot_port_lag_change);
 
 int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond,
-                      const unsigned char *addr, u16 vid)
+                      const unsigned char *addr, u16 vid,
+                      const struct net_device *bridge)
 {
        struct ocelot_lag_fdb *fdb;
        int lag, err;
@@ -2553,11 +2711,15 @@ int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond,
        if (!fdb)
                return -ENOMEM;
 
+       mutex_lock(&ocelot->fwd_domain_lock);
+
+       if (!vid)
+               vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
        ether_addr_copy(fdb->addr, addr);
        fdb->vid = vid;
        fdb->bond = bond;
 
-       mutex_lock(&ocelot->fwd_domain_lock);
        lag = ocelot_bond_get_id(ocelot, bond);
 
        err = ocelot_mact_learn(ocelot, lag, addr, vid, ENTRYTYPE_LOCKED);
@@ -2575,12 +2737,16 @@ int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond,
 EXPORT_SYMBOL_GPL(ocelot_lag_fdb_add);
 
 int ocelot_lag_fdb_del(struct ocelot *ocelot, struct net_device *bond,
-                      const unsigned char *addr, u16 vid)
+                      const unsigned char *addr, u16 vid,
+                      const struct net_device *bridge)
 {
        struct ocelot_lag_fdb *fdb, *tmp;
 
        mutex_lock(&ocelot->fwd_domain_lock);
 
+       if (!vid)
+               vid = ocelot_vlan_unaware_pvid(ocelot, bridge);
+
        list_for_each_entry_safe(fdb, tmp, &ocelot->lag_fdbs, list) {
                if (!ether_addr_equal(fdb->addr, addr) || fdb->vid != vid ||
                    fdb->bond != bond)
@@ -2832,7 +2998,7 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot)
 
        /* Configure the CPU port to be VLAN aware */
        ocelot_write_gix(ocelot,
-                        ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_VLAN_UNAWARE_PVID) |
+                        ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_STANDALONE_PVID) |
                         ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
                         ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
                         ANA_PORT_VLAN_CFG, cpu);
index 5277c4b..f8dc0d7 100644 (file)
@@ -26,7 +26,7 @@
 #include "ocelot_rew.h"
 #include "ocelot_qs.h"
 
-#define OCELOT_VLAN_UNAWARE_PVID 0
+#define OCELOT_STANDALONE_PVID 0
 #define OCELOT_BUFFER_CELL_SZ 60
 
 #define OCELOT_STATS_CHECK_DELAY (2 * HZ)
@@ -81,6 +81,9 @@ struct ocelot_multicast {
        struct ocelot_pgid *pgid;
 };
 
+int ocelot_bridge_num_find(struct ocelot *ocelot,
+                          const struct net_device *bridge);
+
 int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
                            bool is_static, void *data);
 int ocelot_mact_learn(struct ocelot *ocelot, int port,
index 142e897..3ccec48 100644 (file)
@@ -107,16 +107,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot,
                                struct ocelot_port *port)
 {
        ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac,
-                         OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
+                         OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
        ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac,
-                         OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
+                         OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
 }
 
 static void ocelot_mrp_del_mac(struct ocelot *ocelot,
                               struct ocelot_port *port)
 {
-       ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID);
-       ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID);
+       ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_STANDALONE_PVID);
+       ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_STANDALONE_PVID);
 }
 
 int ocelot_mrp_add(struct ocelot *ocelot, int port,
index e271b62..cfe767d 100644 (file)
@@ -419,7 +419,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 == OCELOT_VLAN_UNAWARE_PVID)
+       if (vid == OCELOT_STANDALONE_PVID)
                return 0;
 
        ret = ocelot_vlan_del(ocelot, port, vid);
@@ -559,7 +559,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_VLAN_UNAWARE_PVID;
+       w.forget.vid = OCELOT_STANDALONE_PVID;
        w.type = OCELOT_MACT_FORGET;
 
        return ocelot_enqueue_mact_action(ocelot, &w);
@@ -573,7 +573,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_VLAN_UNAWARE_PVID;
+       w.learn.vid = OCELOT_STANDALONE_PVID;
        w.learn.pgid = PGID_CPU;
        w.learn.entry_type = ENTRYTYPE_LOCKED;
        w.type = OCELOT_MACT_LEARN;
@@ -608,9 +608,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_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
+                         OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
        /* Then forget the previous one. */
-       ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID);
+       ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_STANDALONE_PVID);
 
        eth_hw_addr_set(dev, addr->sa_data);
        return 0;
@@ -662,10 +662,11 @@ static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                               struct netlink_ext_ack *extack)
 {
        struct ocelot_port_private *priv = netdev_priv(dev);
-       struct ocelot *ocelot = priv->port.ocelot;
+       struct ocelot_port *ocelot_port = &priv->port;
+       struct ocelot *ocelot = ocelot_port->ocelot;
        int port = priv->chip_port;
 
-       return ocelot_fdb_add(ocelot, port, addr, vid);
+       return ocelot_fdb_add(ocelot, port, addr, vid, ocelot_port->bridge);
 }
 
 static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
@@ -673,10 +674,11 @@ static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
                               const unsigned char *addr, u16 vid)
 {
        struct ocelot_port_private *priv = netdev_priv(dev);
-       struct ocelot *ocelot = priv->port.ocelot;
+       struct ocelot_port *ocelot_port = &priv->port;
+       struct ocelot *ocelot = ocelot_port->ocelot;
        int port = priv->chip_port;
 
-       return ocelot_fdb_del(ocelot, port, addr, vid);
+       return ocelot_fdb_del(ocelot, port, addr, vid, ocelot_port->bridge);
 }
 
 static int ocelot_port_fdb_dump(struct sk_buff *skb,
@@ -988,7 +990,7 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev,
        struct ocelot *ocelot = ocelot_port->ocelot;
        int port = priv->chip_port;
 
-       return ocelot_port_mdb_add(ocelot, port, mdb);
+       return ocelot_port_mdb_add(ocelot, port, mdb, ocelot_port->bridge);
 }
 
 static int ocelot_port_obj_del_mdb(struct net_device *dev,
@@ -999,7 +1001,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
        struct ocelot *ocelot = ocelot_port->ocelot;
        int port = priv->chip_port;
 
-       return ocelot_port_mdb_del(ocelot, port, mdb);
+       return ocelot_port_mdb_del(ocelot, port, mdb, ocelot_port->bridge);
 }
 
 static int ocelot_port_obj_mrp_add(struct net_device *dev,
@@ -1173,6 +1175,33 @@ static int ocelot_switchdev_unsync(struct ocelot *ocelot, int port)
        return 0;
 }
 
+static int ocelot_bridge_num_get(struct ocelot *ocelot,
+                                const struct net_device *bridge_dev)
+{
+       int bridge_num = ocelot_bridge_num_find(ocelot, bridge_dev);
+
+       if (bridge_num < 0) {
+               /* First port that offloads this bridge */
+               bridge_num = find_first_zero_bit(&ocelot->bridges,
+                                                ocelot->num_phys_ports);
+
+               set_bit(bridge_num, &ocelot->bridges);
+       }
+
+       return bridge_num;
+}
+
+static void ocelot_bridge_num_put(struct ocelot *ocelot,
+                                 const struct net_device *bridge_dev,
+                                 int bridge_num)
+{
+       /* Check if the bridge is still in use, otherwise it is time
+        * to clean it up so we can reuse this bridge_num later.
+        */
+       if (!ocelot_bridge_num_find(ocelot, bridge_dev))
+               clear_bit(bridge_num, &ocelot->bridges);
+}
+
 static int ocelot_netdevice_bridge_join(struct net_device *dev,
                                        struct net_device *brport_dev,
                                        struct net_device *bridge,
@@ -1182,9 +1211,14 @@ static int ocelot_netdevice_bridge_join(struct net_device *dev,
        struct ocelot_port *ocelot_port = &priv->port;
        struct ocelot *ocelot = ocelot_port->ocelot;
        int port = priv->chip_port;
-       int err;
+       int bridge_num, err;
 
-       ocelot_port_bridge_join(ocelot, port, bridge);
+       bridge_num = ocelot_bridge_num_get(ocelot, bridge);
+
+       err = ocelot_port_bridge_join(ocelot, port, bridge, bridge_num,
+                                     extack);
+       if (err)
+               goto err_join;
 
        err = switchdev_bridge_port_offload(brport_dev, dev, priv,
                                            &ocelot_switchdev_nb,
@@ -1205,6 +1239,8 @@ err_switchdev_sync:
                                        &ocelot_switchdev_blocking_nb);
 err_switchdev_offload:
        ocelot_port_bridge_leave(ocelot, port, bridge);
+err_join:
+       ocelot_bridge_num_put(ocelot, bridge, bridge_num);
        return err;
 }
 
@@ -1225,6 +1261,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev,
        struct ocelot_port_private *priv = netdev_priv(dev);
        struct ocelot_port *ocelot_port = &priv->port;
        struct ocelot *ocelot = ocelot_port->ocelot;
+       int bridge_num = ocelot_port->bridge_num;
        int port = priv->chip_port;
        int err;
 
@@ -1233,6 +1270,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev,
                return err;
 
        ocelot_port_bridge_leave(ocelot, port, bridge);
+       ocelot_bridge_num_put(ocelot, bridge, bridge_num);
 
        return 0;
 }
@@ -1700,7 +1738,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_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
+                         OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED);
 
        ocelot_init_port(ocelot, port);
 
index 939a1be..3ed117e 100644 (file)
@@ -32,31 +32,29 @@ int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto);
 
 void dsa_tag_8021q_unregister(struct dsa_switch *ds);
 
-struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
-                              u16 tpid, u16 tci);
+int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
+                             struct dsa_bridge bridge);
 
-void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id);
+void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port,
+                               struct dsa_bridge bridge);
 
-int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
-                                       struct dsa_bridge bridge);
+struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
+                              u16 tpid, u16 tci);
 
-void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,
-                                          struct dsa_bridge bridge);
+void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id,
+                  int *vbid);
 
-u16 dsa_8021q_bridge_tx_fwd_offload_vid(unsigned int bridge_num);
+struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master,
+                                                  int vbid);
 
-u16 dsa_tag_8021q_tx_vid(const struct dsa_port *dp);
+u16 dsa_tag_8021q_bridge_vid(unsigned int bridge_num);
 
-u16 dsa_tag_8021q_rx_vid(const struct dsa_port *dp);
+u16 dsa_tag_8021q_standalone_vid(const struct dsa_port *dp);
 
 int dsa_8021q_rx_switch_id(u16 vid);
 
 int dsa_8021q_rx_source_port(u16 vid);
 
-bool vid_is_dsa_8021q_rxvlan(u16 vid);
-
-bool vid_is_dsa_8021q_txvlan(u16 vid);
-
 bool vid_is_dsa_8021q(u16 vid);
 
 #endif /* _NET_DSA_8021Q_H */
index 01faba8..cfedcfb 100644 (file)
@@ -341,11 +341,28 @@ struct dsa_link {
        struct list_head list;
 };
 
+enum dsa_db_type {
+       DSA_DB_PORT,
+       DSA_DB_LAG,
+       DSA_DB_BRIDGE,
+};
+
+struct dsa_db {
+       enum dsa_db_type type;
+
+       union {
+               const struct dsa_port *dp;
+               struct dsa_lag lag;
+               struct dsa_bridge bridge;
+       };
+};
+
 struct dsa_mac_addr {
        unsigned char addr[ETH_ALEN];
        u16 vid;
        refcount_t refcount;
        struct list_head list;
+       struct dsa_db db;
 };
 
 struct dsa_vlan {
@@ -409,6 +426,13 @@ struct dsa_switch {
         */
        u32                     mtu_enforcement_ingress:1;
 
+       /* Drivers that isolate the FDBs of multiple bridges must set this
+        * to true to receive the bridge as an argument in .port_fdb_{add,del}
+        * and .port_mdb_{add,del}. Otherwise, the bridge.num will always be
+        * passed as zero.
+        */
+       u32                     fdb_isolation:1;
+
        /* Listener for switch fabric events */
        struct notifier_block   nb;
 
@@ -913,7 +937,8 @@ struct dsa_switch_ops {
        int     (*set_ageing_time)(struct dsa_switch *ds, unsigned int msecs);
        int     (*port_bridge_join)(struct dsa_switch *ds, int port,
                                    struct dsa_bridge bridge,
-                                   bool *tx_fwd_offload);
+                                   bool *tx_fwd_offload,
+                                   struct netlink_ext_ack *extack);
        void    (*port_bridge_leave)(struct dsa_switch *ds, int port,
                                     struct dsa_bridge bridge);
        void    (*port_stp_state_set)(struct dsa_switch *ds, int port,
@@ -941,23 +966,29 @@ struct dsa_switch_ops {
         * Forwarding database
         */
        int     (*port_fdb_add)(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid);
+                               const unsigned char *addr, u16 vid,
+                               struct dsa_db db);
        int     (*port_fdb_del)(struct dsa_switch *ds, int port,
-                               const unsigned char *addr, u16 vid);
+                               const unsigned char *addr, u16 vid,
+                               struct dsa_db db);
        int     (*port_fdb_dump)(struct dsa_switch *ds, int port,
                                 dsa_fdb_dump_cb_t *cb, void *data);
        int     (*lag_fdb_add)(struct dsa_switch *ds, struct dsa_lag lag,
-                              const unsigned char *addr, u16 vid);
+                              const unsigned char *addr, u16 vid,
+                              struct dsa_db db);
        int     (*lag_fdb_del)(struct dsa_switch *ds, struct dsa_lag lag,
-                              const unsigned char *addr, u16 vid);
+                              const unsigned char *addr, u16 vid,
+                              struct dsa_db db);
 
        /*
         * Multicast database
         */
        int     (*port_mdb_add)(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb);
+                               const struct switchdev_obj_port_mdb *mdb,
+                               struct dsa_db db);
        int     (*port_mdb_del)(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb);
+                               const struct switchdev_obj_port_mdb *mdb,
+                               struct dsa_db db);
        /*
         * RXNFC
         */
@@ -991,7 +1022,8 @@ struct dsa_switch_ops {
         */
        int     (*crosschip_bridge_join)(struct dsa_switch *ds, int tree_index,
                                         int sw_index, int port,
-                                        struct dsa_bridge bridge);
+                                        struct dsa_bridge bridge,
+                                        struct netlink_ext_ack *extack);
        void    (*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index,
                                          int sw_index, int port,
                                          struct dsa_bridge bridge);
index dd4fc34..ee3c596 100644 (file)
@@ -668,6 +668,7 @@ struct ocelot_port {
        u16                             mrp_ring_id;
 
        struct net_device               *bridge;
+       int                             bridge_num;
        u8                              stp_state;
 
        int                             speed;
@@ -713,6 +714,8 @@ struct ocelot {
        enum ocelot_tag_prefix          npi_inj_prefix;
        enum ocelot_tag_prefix          npi_xtr_prefix;
 
+       unsigned long                   bridges;
+
        struct list_head                multicast;
        struct list_head                pgids;
 
@@ -846,6 +849,9 @@ void ocelot_deinit(struct ocelot *ocelot);
 void ocelot_init_port(struct ocelot *ocelot, int port);
 void ocelot_deinit_port(struct ocelot *ocelot, int port);
 
+void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port);
+void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port);
+
 /* DSA callbacks */
 void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data);
 void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data);
@@ -863,21 +869,24 @@ int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
                                 struct switchdev_brport_flags val);
 void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
                              struct switchdev_brport_flags val);
-void ocelot_port_bridge_join(struct ocelot *ocelot, int port,
-                            struct net_device *bridge);
+int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
+                           struct net_device *bridge, int bridge_num,
+                           struct netlink_ext_ack *extack);
 void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
                              struct net_device *bridge);
 int ocelot_mact_flush(struct ocelot *ocelot, int port);
 int ocelot_fdb_dump(struct ocelot *ocelot, int port,
                    dsa_fdb_dump_cb_t *cb, void *data);
-int ocelot_fdb_add(struct ocelot *ocelot, int port,
-                  const unsigned char *addr, u16 vid);
-int ocelot_fdb_del(struct ocelot *ocelot, int port,
-                  const unsigned char *addr, u16 vid);
+int ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr,
+                  u16 vid, const struct net_device *bridge);
+int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr,
+                  u16 vid, const struct net_device *bridge);
 int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond,
-                      const unsigned char *addr, u16 vid);
+                      const unsigned char *addr, u16 vid,
+                      const struct net_device *bridge);
 int ocelot_lag_fdb_del(struct ocelot *ocelot, struct net_device *bond,
-                      const unsigned char *addr, u16 vid);
+                      const unsigned char *addr, u16 vid,
+                      const struct net_device *bridge);
 int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
                        bool untagged, struct netlink_ext_ack *extack);
 int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
@@ -901,9 +910,11 @@ int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
 int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
                            struct flow_cls_offload *f, bool ingress);
 int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
-                       const struct switchdev_obj_port_mdb *mdb);
+                       const struct switchdev_obj_port_mdb *mdb,
+                       const struct net_device *bridge);
 int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
-                       const struct switchdev_obj_port_mdb *mdb);
+                       const struct switchdev_obj_port_mdb *mdb,
+                       const struct net_device *bridge);
 int ocelot_port_lag_join(struct ocelot *ocelot, int port,
                         struct net_device *bond,
                         struct netdev_lag_upper_info *info);
index 1ba93af..07c0ad5 100644 (file)
@@ -59,6 +59,7 @@ struct dsa_notifier_bridge_info {
        int sw_index;
        int port;
        bool tx_fwd_offload;
+       struct netlink_ext_ack *extack;
 };
 
 /* DSA_NOTIFIER_FDB_* */
@@ -67,6 +68,7 @@ struct dsa_notifier_fdb_info {
        int port;
        const unsigned char *addr;
        u16 vid;
+       struct dsa_db db;
 };
 
 /* DSA_NOTIFIER_LAG_FDB_* */
@@ -74,6 +76,7 @@ struct dsa_notifier_lag_fdb_info {
        struct dsa_lag *lag;
        const unsigned char *addr;
        u16 vid;
+       struct dsa_db db;
 };
 
 /* DSA_NOTIFIER_MDB_* */
@@ -81,6 +84,7 @@ struct dsa_notifier_mdb_info {
        const struct switchdev_obj_port_mdb *mdb;
        int sw_index;
        int port;
+       struct dsa_db db;
 };
 
 /* DSA_NOTIFIER_LAG_* */
@@ -522,10 +526,6 @@ struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst,
                                        const struct net_device *br);
 
 /* tag_8021q.c */
-int dsa_tag_8021q_bridge_join(struct dsa_switch *ds,
-                             struct dsa_notifier_bridge_info *info);
-int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds,
-                              struct dsa_notifier_bridge_info *info);
 int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds,
                                  struct dsa_notifier_tag_8021q_vlan_info *info);
 int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds,
index adab159..d9da425 100644 (file)
@@ -328,6 +328,7 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
                .tree_index = dp->ds->dst->index,
                .sw_index = dp->ds->index,
                .port = dp->index,
+               .extack = extack,
        };
        struct net_device *dev = dp->slave;
        struct net_device *brport_dev;
@@ -798,8 +799,19 @@ int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
                .port = dp->index,
                .addr = addr,
                .vid = vid,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
 
+       /* Refcounting takes bridge.num as a key, and should be global for all
+        * bridges in the absence of FDB isolation, and per bridge otherwise.
+        * Force the bridge.num to zero here in the absence of FDB isolation.
+        */
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
 }
 
@@ -811,9 +823,15 @@ int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
                .port = dp->index,
                .addr = addr,
                .vid = vid,
-
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
 }
 
@@ -825,6 +843,10 @@ int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr,
                .port = dp->index,
                .addr = addr,
                .vid = vid,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
        struct dsa_port *cpu_dp = dp->cpu_dp;
        int err;
@@ -839,6 +861,9 @@ int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr,
                        return err;
        }
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info);
 }
 
@@ -850,6 +875,10 @@ int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr,
                .port = dp->index,
                .addr = addr,
                .vid = vid,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
        struct dsa_port *cpu_dp = dp->cpu_dp;
        int err;
@@ -860,6 +889,9 @@ int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr,
                        return err;
        }
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info);
 }
 
@@ -870,8 +902,15 @@ int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr,
                .lag = dp->lag,
                .addr = addr,
                .vid = vid,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_ADD, &info);
 }
 
@@ -882,8 +921,15 @@ int dsa_port_lag_fdb_del(struct dsa_port *dp, const unsigned char *addr,
                .lag = dp->lag,
                .addr = addr,
                .vid = vid,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_DEL, &info);
 }
 
@@ -905,8 +951,15 @@ int dsa_port_mdb_add(const struct dsa_port *dp,
                .sw_index = dp->ds->index,
                .port = dp->index,
                .mdb = mdb,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
 }
 
@@ -917,8 +970,15 @@ int dsa_port_mdb_del(const struct dsa_port *dp,
                .sw_index = dp->ds->index,
                .port = dp->index,
                .mdb = mdb,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
 }
 
@@ -929,6 +989,10 @@ int dsa_port_host_mdb_add(const struct dsa_port *dp,
                .sw_index = dp->ds->index,
                .port = dp->index,
                .mdb = mdb,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
        struct dsa_port *cpu_dp = dp->cpu_dp;
        int err;
@@ -937,6 +1001,9 @@ int dsa_port_host_mdb_add(const struct dsa_port *dp,
        if (err)
                return err;
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info);
 }
 
@@ -947,6 +1014,10 @@ int dsa_port_host_mdb_del(const struct dsa_port *dp,
                .sw_index = dp->ds->index,
                .port = dp->index,
                .mdb = mdb,
+               .db = {
+                       .type = DSA_DB_BRIDGE,
+                       .bridge = *dp->bridge,
+               },
        };
        struct dsa_port *cpu_dp = dp->cpu_dp;
        int err;
@@ -955,6 +1026,9 @@ int dsa_port_host_mdb_del(const struct dsa_port *dp,
        if (err)
                return err;
 
+       if (!dp->ds->fdb_isolation)
+               info.db.bridge.num = 0;
+
        return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info);
 }
 
index 0c2961c..327d66b 100644 (file)
@@ -96,7 +96,8 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds,
                        return -EOPNOTSUPP;
 
                err = ds->ops->port_bridge_join(ds, info->port, info->bridge,
-                                               &info->tx_fwd_offload);
+                                               &info->tx_fwd_offload,
+                                               info->extack);
                if (err)
                        return err;
        }
@@ -105,12 +106,13 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds,
            ds->ops->crosschip_bridge_join) {
                err = ds->ops->crosschip_bridge_join(ds, info->tree_index,
                                                     info->sw_index,
-                                                    info->port, info->bridge);
+                                                    info->port, info->bridge,
+                                                    info->extack);
                if (err)
                        return err;
        }
 
-       return dsa_tag_8021q_bridge_join(ds, info);
+       return 0;
 }
 
 static int dsa_switch_sync_vlan_filtering(struct dsa_switch *ds,
@@ -186,7 +188,7 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
                        return err;
        }
 
-       return dsa_tag_8021q_bridge_leave(ds, info);
+       return 0;
 }
 
 /* Matches for all upstream-facing ports (the CPU port and all upstream-facing
@@ -210,21 +212,41 @@ static bool dsa_port_host_address_match(struct dsa_port *dp,
        return false;
 }
 
+static bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b)
+{
+       if (a->type != b->type)
+               return false;
+
+       switch (a->type) {
+       case DSA_DB_PORT:
+               return a->dp == b->dp;
+       case DSA_DB_LAG:
+               return a->lag.dev == b->lag.dev;
+       case DSA_DB_BRIDGE:
+               return a->bridge.num == b->bridge.num;
+       default:
+               WARN_ON(1);
+               return false;
+       }
+}
+
 static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list,
-                                             const unsigned char *addr,
-                                             u16 vid)
+                                             const unsigned char *addr, u16 vid,
+                                             struct dsa_db db)
 {
        struct dsa_mac_addr *a;
 
        list_for_each_entry(a, addr_list, list)
-               if (ether_addr_equal(a->addr, addr) && a->vid == vid)
+               if (ether_addr_equal(a->addr, addr) && a->vid == vid &&
+                   dsa_db_equal(&a->db, &db))
                        return a;
 
        return NULL;
 }
 
 static int dsa_port_do_mdb_add(struct dsa_port *dp,
-                              const struct switchdev_obj_port_mdb *mdb)
+                              const struct switchdev_obj_port_mdb *mdb,
+                              struct dsa_db db)
 {
        struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
@@ -233,11 +255,11 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp,
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
-               return ds->ops->port_mdb_add(ds, port, mdb);
+               return ds->ops->port_mdb_add(ds, port, mdb, db);
 
        mutex_lock(&dp->addr_lists_lock);
 
-       a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
+       a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);
        if (a) {
                refcount_inc(&a->refcount);
                goto out;
@@ -249,7 +271,7 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp,
                goto out;
        }
 
-       err = ds->ops->port_mdb_add(ds, port, mdb);
+       err = ds->ops->port_mdb_add(ds, port, mdb, db);
        if (err) {
                kfree(a);
                goto out;
@@ -257,6 +279,7 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp,
 
        ether_addr_copy(a->addr, mdb->addr);
        a->vid = mdb->vid;
+       a->db = db;
        refcount_set(&a->refcount, 1);
        list_add_tail(&a->list, &dp->mdbs);
 
@@ -267,7 +290,8 @@ out:
 }
 
 static int dsa_port_do_mdb_del(struct dsa_port *dp,
-                              const struct switchdev_obj_port_mdb *mdb)
+                              const struct switchdev_obj_port_mdb *mdb,
+                              struct dsa_db db)
 {
        struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
@@ -276,11 +300,11 @@ static int dsa_port_do_mdb_del(struct dsa_port *dp,
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
-               return ds->ops->port_mdb_del(ds, port, mdb);
+               return ds->ops->port_mdb_del(ds, port, mdb, db);
 
        mutex_lock(&dp->addr_lists_lock);
 
-       a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
+       a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);
        if (!a) {
                err = -ENOENT;
                goto out;
@@ -289,7 +313,7 @@ static int dsa_port_do_mdb_del(struct dsa_port *dp,
        if (!refcount_dec_and_test(&a->refcount))
                goto out;
 
-       err = ds->ops->port_mdb_del(ds, port, mdb);
+       err = ds->ops->port_mdb_del(ds, port, mdb, db);
        if (err) {
                refcount_set(&a->refcount, 1);
                goto out;
@@ -305,7 +329,7 @@ out:
 }
 
 static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
-                              u16 vid)
+                              u16 vid, struct dsa_db db)
 {
        struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
@@ -314,11 +338,11 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
-               return ds->ops->port_fdb_add(ds, port, addr, vid);
+               return ds->ops->port_fdb_add(ds, port, addr, vid, db);
 
        mutex_lock(&dp->addr_lists_lock);
 
-       a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
+       a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);
        if (a) {
                refcount_inc(&a->refcount);
                goto out;
@@ -330,7 +354,7 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
                goto out;
        }
 
-       err = ds->ops->port_fdb_add(ds, port, addr, vid);
+       err = ds->ops->port_fdb_add(ds, port, addr, vid, db);
        if (err) {
                kfree(a);
                goto out;
@@ -338,6 +362,7 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
 
        ether_addr_copy(a->addr, addr);
        a->vid = vid;
+       a->db = db;
        refcount_set(&a->refcount, 1);
        list_add_tail(&a->list, &dp->fdbs);
 
@@ -348,7 +373,7 @@ out:
 }
 
 static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
-                              u16 vid)
+                              u16 vid, struct dsa_db db)
 {
        struct dsa_switch *ds = dp->ds;
        struct dsa_mac_addr *a;
@@ -357,11 +382,11 @@ static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
 
        /* No need to bother with refcounting for user ports */
        if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
-               return ds->ops->port_fdb_del(ds, port, addr, vid);
+               return ds->ops->port_fdb_del(ds, port, addr, vid, db);
 
        mutex_lock(&dp->addr_lists_lock);
 
-       a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
+       a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);
        if (!a) {
                err = -ENOENT;
                goto out;
@@ -370,7 +395,7 @@ static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
        if (!refcount_dec_and_test(&a->refcount))
                goto out;
 
-       err = ds->ops->port_fdb_del(ds, port, addr, vid);
+       err = ds->ops->port_fdb_del(ds, port, addr, vid, db);
        if (err) {
                refcount_set(&a->refcount, 1);
                goto out;
@@ -386,14 +411,15 @@ out:
 }
 
 static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag,
-                                    const unsigned char *addr, u16 vid)
+                                    const unsigned char *addr, u16 vid,
+                                    struct dsa_db db)
 {
        struct dsa_mac_addr *a;
        int err = 0;
 
        mutex_lock(&lag->fdb_lock);
 
-       a = dsa_mac_addr_find(&lag->fdbs, addr, vid);
+       a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);
        if (a) {
                refcount_inc(&a->refcount);
                goto out;
@@ -405,7 +431,7 @@ static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag,
                goto out;
        }
 
-       err = ds->ops->lag_fdb_add(ds, *lag, addr, vid);
+       err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db);
        if (err) {
                kfree(a);
                goto out;
@@ -423,14 +449,15 @@ out:
 }
 
 static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag,
-                                    const unsigned char *addr, u16 vid)
+                                    const unsigned char *addr, u16 vid,
+                                    struct dsa_db db)
 {
        struct dsa_mac_addr *a;
        int err = 0;
 
        mutex_lock(&lag->fdb_lock);
 
-       a = dsa_mac_addr_find(&lag->fdbs, addr, vid);
+       a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);
        if (!a) {
                err = -ENOENT;
                goto out;
@@ -439,7 +466,7 @@ static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag,
        if (!refcount_dec_and_test(&a->refcount))
                goto out;
 
-       err = ds->ops->lag_fdb_del(ds, *lag, addr, vid);
+       err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db);
        if (err) {
                refcount_set(&a->refcount, 1);
                goto out;
@@ -466,7 +493,8 @@ static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
        dsa_switch_for_each_port(dp, ds) {
                if (dsa_port_host_address_match(dp, info->sw_index,
                                                info->port)) {
-                       err = dsa_port_do_fdb_add(dp, info->addr, info->vid);
+                       err = dsa_port_do_fdb_add(dp, info->addr, info->vid,
+                                                 info->db);
                        if (err)
                                break;
                }
@@ -487,7 +515,8 @@ static int dsa_switch_host_fdb_del(struct dsa_switch *ds,
        dsa_switch_for_each_port(dp, ds) {
                if (dsa_port_host_address_match(dp, info->sw_index,
                                                info->port)) {
-                       err = dsa_port_do_fdb_del(dp, info->addr, info->vid);
+                       err = dsa_port_do_fdb_del(dp, info->addr, info->vid,
+                                                 info->db);
                        if (err)
                                break;
                }
@@ -505,7 +534,7 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds,
        if (!ds->ops->port_fdb_add)
                return -EOPNOTSUPP;
 
-       return dsa_port_do_fdb_add(dp, info->addr, info->vid);
+       return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db);
 }
 
 static int dsa_switch_fdb_del(struct dsa_switch *ds,
@@ -517,7 +546,7 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,
        if (!ds->ops->port_fdb_del)
                return -EOPNOTSUPP;
 
-       return dsa_port_do_fdb_del(dp, info->addr, info->vid);
+       return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db);
 }
 
 static int dsa_switch_lag_fdb_add(struct dsa_switch *ds,
@@ -532,7 +561,8 @@ static int dsa_switch_lag_fdb_add(struct dsa_switch *ds,
        dsa_switch_for_each_port(dp, ds)
                if (dsa_port_offloads_lag(dp, info->lag))
                        return dsa_switch_do_lag_fdb_add(ds, info->lag,
-                                                        info->addr, info->vid);
+                                                        info->addr, info->vid,
+                                                        info->db);
 
        return 0;
 }
@@ -549,7 +579,8 @@ static int dsa_switch_lag_fdb_del(struct dsa_switch *ds,
        dsa_switch_for_each_port(dp, ds)
                if (dsa_port_offloads_lag(dp, info->lag))
                        return dsa_switch_do_lag_fdb_del(ds, info->lag,
-                                                        info->addr, info->vid);
+                                                        info->addr, info->vid,
+                                                        info->db);
 
        return 0;
 }
@@ -604,7 +635,7 @@ static int dsa_switch_mdb_add(struct dsa_switch *ds,
        if (!ds->ops->port_mdb_add)
                return -EOPNOTSUPP;
 
-       return dsa_port_do_mdb_add(dp, info->mdb);
+       return dsa_port_do_mdb_add(dp, info->mdb, info->db);
 }
 
 static int dsa_switch_mdb_del(struct dsa_switch *ds,
@@ -616,7 +647,7 @@ static int dsa_switch_mdb_del(struct dsa_switch *ds,
        if (!ds->ops->port_mdb_del)
                return -EOPNOTSUPP;
 
-       return dsa_port_do_mdb_del(dp, info->mdb);
+       return dsa_port_do_mdb_del(dp, info->mdb, info->db);
 }
 
 static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
@@ -631,7 +662,7 @@ static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
        dsa_switch_for_each_port(dp, ds) {
                if (dsa_port_host_address_match(dp, info->sw_index,
                                                info->port)) {
-                       err = dsa_port_do_mdb_add(dp, info->mdb);
+                       err = dsa_port_do_mdb_add(dp, info->mdb, info->db);
                        if (err)
                                break;
                }
@@ -652,7 +683,7 @@ static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
        dsa_switch_for_each_port(dp, ds) {
                if (dsa_port_host_address_match(dp, info->sw_index,
                                                info->port)) {
-                       err = dsa_port_do_mdb_del(dp, info->mdb);
+                       err = dsa_port_do_mdb_del(dp, info->mdb, info->db);
                        if (err)
                                break;
                }
index 114f663..a786569 100644 (file)
  *
  * | 11  | 10  |  9  |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
  * +-----------+-----+-----------------+-----------+-----------------------+
- * |    DIR    | VBID|    SWITCH_ID    |   VBID    |          PORT         |
+ * |    RSV    | VBID|    SWITCH_ID    |   VBID    |          PORT         |
  * +-----------+-----+-----------------+-----------+-----------------------+
  *
- * DIR - VID[11:10]:
- *     Direction flags.
- *     * 1 (0b01) for RX VLAN,
- *     * 2 (0b10) for TX VLAN.
- *     These values make the special VIDs of 0, 1 and 4095 to be left
- *     unused by this coding scheme.
+ * RSV - VID[11:10]:
+ *     Reserved. Must be set to 3 (0b11).
  *
  * SWITCH_ID - VID[8:6]:
  *     Index of switch within DSA tree. Must be between 0 and 7.
  * VBID - { VID[9], VID[5:4] }:
  *     Virtual bridge ID. If between 1 and 7, packet targets the broadcast
  *     domain of a bridge. If transmitted as zero, packet targets a single
- *     port. Field only valid on transmit, must be ignored on receive.
+ *     port.
  *
  * PORT - VID[3:0]:
  *     Index of switch port. Must be between 0 and 15.
  */
 
-#define DSA_8021Q_DIR_SHIFT            10
-#define DSA_8021Q_DIR_MASK             GENMASK(11, 10)
-#define DSA_8021Q_DIR(x)               (((x) << DSA_8021Q_DIR_SHIFT) & \
-                                                DSA_8021Q_DIR_MASK)
-#define DSA_8021Q_DIR_RX               DSA_8021Q_DIR(1)
-#define DSA_8021Q_DIR_TX               DSA_8021Q_DIR(2)
+#define DSA_8021Q_RSV_VAL              3
+#define DSA_8021Q_RSV_SHIFT            10
+#define DSA_8021Q_RSV_MASK             GENMASK(11, 10)
+#define DSA_8021Q_RSV                  ((DSA_8021Q_RSV_VAL << DSA_8021Q_RSV_SHIFT) & \
+                                                              DSA_8021Q_RSV_MASK)
 
 #define DSA_8021Q_SWITCH_ID_SHIFT      6
 #define DSA_8021Q_SWITCH_ID_MASK       GENMASK(8, 6)
 #define DSA_8021Q_PORT(x)              (((x) << DSA_8021Q_PORT_SHIFT) & \
                                                 DSA_8021Q_PORT_MASK)
 
-u16 dsa_8021q_bridge_tx_fwd_offload_vid(unsigned int bridge_num)
+u16 dsa_tag_8021q_bridge_vid(unsigned int bridge_num)
 {
        /* The VBID value of 0 is reserved for precise TX, but it is also
         * reserved/invalid for the bridge_num, so all is well.
         */
-       return DSA_8021Q_DIR_TX | DSA_8021Q_VBID(bridge_num);
+       return DSA_8021Q_RSV | DSA_8021Q_VBID(bridge_num);
 }
-EXPORT_SYMBOL_GPL(dsa_8021q_bridge_tx_fwd_offload_vid);
-
-/* Returns the VID to be inserted into the frame from xmit for switch steering
- * instructions on egress. Encodes switch ID and port ID.
- */
-u16 dsa_tag_8021q_tx_vid(const struct dsa_port *dp)
-{
-       return DSA_8021Q_DIR_TX | DSA_8021Q_SWITCH_ID(dp->ds->index) |
-              DSA_8021Q_PORT(dp->index);
-}
-EXPORT_SYMBOL_GPL(dsa_tag_8021q_tx_vid);
+EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_vid);
 
 /* Returns the VID that will be installed as pvid for this switch port, sent as
  * tagged egress towards the CPU port and decoded by the rcv function.
  */
-u16 dsa_tag_8021q_rx_vid(const struct dsa_port *dp)
+u16 dsa_tag_8021q_standalone_vid(const struct dsa_port *dp)
 {
-       return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(dp->ds->index) |
+       return DSA_8021Q_RSV | DSA_8021Q_SWITCH_ID(dp->ds->index) |
               DSA_8021Q_PORT(dp->index);
 }
-EXPORT_SYMBOL_GPL(dsa_tag_8021q_rx_vid);
+EXPORT_SYMBOL_GPL(dsa_tag_8021q_standalone_vid);
 
 /* Returns the decoded switch ID from the RX VID. */
 int dsa_8021q_rx_switch_id(u16 vid)
@@ -110,21 +95,20 @@ int dsa_8021q_rx_source_port(u16 vid)
 }
 EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port);
 
-bool vid_is_dsa_8021q_rxvlan(u16 vid)
+/* Returns the decoded VBID from the RX VID. */
+static int dsa_tag_8021q_rx_vbid(u16 vid)
 {
-       return (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_RX;
-}
-EXPORT_SYMBOL_GPL(vid_is_dsa_8021q_rxvlan);
+       u16 vbid_hi = (vid & DSA_8021Q_VBID_HI_MASK) >> DSA_8021Q_VBID_HI_SHIFT;
+       u16 vbid_lo = (vid & DSA_8021Q_VBID_LO_MASK) >> DSA_8021Q_VBID_LO_SHIFT;
 
-bool vid_is_dsa_8021q_txvlan(u16 vid)
-{
-       return (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_TX;
+       return (vbid_hi << 2) | vbid_lo;
 }
-EXPORT_SYMBOL_GPL(vid_is_dsa_8021q_txvlan);
 
 bool vid_is_dsa_8021q(u16 vid)
 {
-       return vid_is_dsa_8021q_rxvlan(vid) || vid_is_dsa_8021q_txvlan(vid);
+       u16 rsv = (vid & DSA_8021Q_RSV_MASK) >> DSA_8021Q_RSV_SHIFT;
+
+       return rsv == DSA_8021Q_RSV_VAL;
 }
 EXPORT_SYMBOL_GPL(vid_is_dsa_8021q);
 
@@ -242,12 +226,8 @@ int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds,
                        u16 flags = 0;
 
                        if (dsa_port_is_user(dp))
-                               flags |= BRIDGE_VLAN_INFO_UNTAGGED;
-
-                       if (vid_is_dsa_8021q_rxvlan(info->vid) &&
-                           dsa_8021q_rx_switch_id(info->vid) == ds->index &&
-                           dsa_8021q_rx_source_port(info->vid) == dp->index)
-                               flags |= BRIDGE_VLAN_INFO_PVID;
+                               flags |= BRIDGE_VLAN_INFO_UNTAGGED |
+                                        BRIDGE_VLAN_INFO_PVID;
 
                        err = dsa_port_do_tag_8021q_vlan_add(dp, info->vid,
                                                             flags);
@@ -279,162 +259,78 @@ int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds,
        return 0;
 }
 
-/* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single
- * front-panel switch port (here swp0).
+/* There are 2 ways of offloading tag_8021q VLANs.
  *
- * Port identification through VLAN (802.1Q) tags has different requirements
- * for it to work effectively:
- *  - On RX (ingress from network): each front-panel port must have a pvid
- *    that uniquely identifies it, and the egress of this pvid must be tagged
- *    towards the CPU port, so that software can recover the source port based
- *    on the VID in the frame. But this would only work for standalone ports;
- *    if bridged, this VLAN setup would break autonomous forwarding and would
- *    force all switched traffic to pass through the CPU. So we must also make
- *    the other front-panel ports members of this VID we're adding, albeit
- *    we're not making it their PVID (they'll still have their own).
- *  - On TX (ingress from CPU and towards network) we are faced with a problem.
- *    If we were to tag traffic (from within DSA) with the port's pvid, all
- *    would be well, assuming the switch ports were standalone. Frames would
- *    have no choice but to be directed towards the correct front-panel port.
- *    But because we also want the RX VLAN to not break bridging, then
- *    inevitably that means that we have to give them a choice (of what
- *    front-panel port to go out on), and therefore we cannot steer traffic
- *    based on the RX VID. So what we do is simply install one more VID on the
- *    front-panel and CPU ports, and profit off of the fact that steering will
- *    work just by virtue of the fact that there is only one other port that's
- *    a member of the VID we're tagging the traffic with - the desired one.
+ * One is to use a hardware TCAM to push the port's standalone VLAN into the
+ * frame when forwarding it to the CPU, as an egress modification rule on the
+ * CPU port. This is preferable because it has no side effects for the
+ * autonomous forwarding path, and accomplishes tag_8021q's primary goal of
+ * identifying the source port of each packet based on VLAN ID.
  *
- * So at the end, each front-panel port will have one RX VID (also the PVID),
- * the RX VID of all other front-panel ports that are in the same bridge, and
- * one TX VID. Whereas the CPU port will have the RX and TX VIDs of all
- * front-panel ports, and on top of that, is also tagged-input and
- * tagged-output (VLAN trunk).
+ * The other is to commit the tag_8021q VLAN as a PVID to the VLAN table, and
+ * to configure the port as VLAN-unaware. This is less preferable because
+ * unique source port identification can only be done for standalone ports;
+ * under a VLAN-unaware bridge, all ports share the same tag_8021q VLAN as
+ * PVID, and under a VLAN-aware bridge, packets received by software will not
+ * have tag_8021q VLANs appended, just bridge VLANs.
  *
- *               CPU port                               CPU port
- * +-------------+-----+-------------+    +-------------+-----+-------------+
- * |  RX VID     |     |             |    |  TX VID     |     |             |
- * |  of swp0    |     |             |    |  of swp0    |     |             |
- * |             +-----+             |    |             +-----+             |
- * |                ^ T              |    |                | Tagged         |
- * |                |                |    |                | ingress        |
- * |    +-------+---+---+-------+    |    |    +-----------+                |
- * |    |       |       |       |    |    |    | Untagged                   |
- * |    |     U v     U v     U v    |    |    v egress                     |
- * | +-----+ +-----+ +-----+ +-----+ |    | +-----+ +-----+ +-----+ +-----+ |
- * | |     | |     | |     | |     | |    | |     | |     | |     | |     | |
- * | |PVID | |     | |     | |     | |    | |     | |     | |     | |     | |
- * +-+-----+-+-----+-+-----+-+-----+-+    +-+-----+-+-----+-+-----+-+-----+-+
- *   swp0    swp1    swp2    swp3           swp0    swp1    swp2    swp3
+ * For tag_8021q implementations of the second type, this method is used to
+ * replace the standalone tag_8021q VLAN of a port with the tag_8021q VLAN to
+ * be used for VLAN-unaware bridging.
  */
-static bool
-dsa_port_tag_8021q_bridge_match(struct dsa_port *dp,
-                               struct dsa_notifier_bridge_info *info)
+int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
+                             struct dsa_bridge bridge)
 {
-       /* Don't match on self */
-       if (dp->ds->dst->index == info->tree_index &&
-           dp->ds->index == info->sw_index &&
-           dp->index == info->port)
-               return false;
-
-       if (dsa_port_is_user(dp))
-               return dsa_port_offloads_bridge(dp, &info->bridge);
-
-       return false;
-}
-
-int dsa_tag_8021q_bridge_join(struct dsa_switch *ds,
-                             struct dsa_notifier_bridge_info *info)
-{
-       struct dsa_switch *targeted_ds;
-       struct dsa_port *targeted_dp;
-       struct dsa_port *dp;
-       u16 targeted_rx_vid;
+       struct dsa_port *dp = dsa_to_port(ds, port);
+       u16 standalone_vid, bridge_vid;
        int err;
 
-       if (!ds->tag_8021q_ctx)
-               return 0;
-
-       targeted_ds = dsa_switch_find(info->tree_index, info->sw_index);
-       targeted_dp = dsa_to_port(targeted_ds, info->port);
-       targeted_rx_vid = dsa_tag_8021q_rx_vid(targeted_dp);
-
-       dsa_switch_for_each_port(dp, ds) {
-               u16 rx_vid = dsa_tag_8021q_rx_vid(dp);
-
-               if (!dsa_port_tag_8021q_bridge_match(dp, info))
-                       continue;
+       /* Delete the standalone VLAN of the port and replace it with a
+        * bridging VLAN
+        */
+       standalone_vid = dsa_tag_8021q_standalone_vid(dp);
+       bridge_vid = dsa_tag_8021q_bridge_vid(bridge.num);
 
-               /* Install the RX VID of the targeted port in our VLAN table */
-               err = dsa_port_tag_8021q_vlan_add(dp, targeted_rx_vid, true);
-               if (err)
-                       return err;
+       err = dsa_port_tag_8021q_vlan_add(dp, bridge_vid, true);
+       if (err)
+               return err;
 
-               /* Install our RX VID into the targeted port's VLAN table */
-               err = dsa_port_tag_8021q_vlan_add(targeted_dp, rx_vid, true);
-               if (err)
-                       return err;
-       }
+       dsa_port_tag_8021q_vlan_del(dp, standalone_vid, false);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_join);
 
-int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds,
-                              struct dsa_notifier_bridge_info *info)
+void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port,
+                               struct dsa_bridge bridge)
 {
-       struct dsa_switch *targeted_ds;
-       struct dsa_port *targeted_dp;
-       struct dsa_port *dp;
-       u16 targeted_rx_vid;
-
-       if (!ds->tag_8021q_ctx)
-               return 0;
-
-       targeted_ds = dsa_switch_find(info->tree_index, info->sw_index);
-       targeted_dp = dsa_to_port(targeted_ds, info->port);
-       targeted_rx_vid = dsa_tag_8021q_rx_vid(targeted_dp);
-
-       dsa_switch_for_each_port(dp, ds) {
-               u16 rx_vid = dsa_tag_8021q_rx_vid(dp);
-
-               if (!dsa_port_tag_8021q_bridge_match(dp, info))
-                       continue;
+       struct dsa_port *dp = dsa_to_port(ds, port);
+       u16 standalone_vid, bridge_vid;
+       int err;
 
-               /* Remove the RX VID of the targeted port from our VLAN table */
-               dsa_port_tag_8021q_vlan_del(dp, targeted_rx_vid, true);
+       /* Delete the bridging VLAN of the port and replace it with a
+        * standalone VLAN
+        */
+       standalone_vid = dsa_tag_8021q_standalone_vid(dp);
+       bridge_vid = dsa_tag_8021q_bridge_vid(bridge.num);
 
-               /* Remove our RX VID from the targeted port's VLAN table */
-               dsa_port_tag_8021q_vlan_del(targeted_dp, rx_vid, true);
+       err = dsa_port_tag_8021q_vlan_add(dp, standalone_vid, false);
+       if (err) {
+               dev_err(ds->dev,
+                       "Failed to delete tag_8021q standalone VLAN %d from port %d: %pe\n",
+                       standalone_vid, port, ERR_PTR(err));
        }
 
-       return 0;
-}
-
-int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
-                                       struct dsa_bridge bridge)
-{
-       u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
-
-       return dsa_port_tag_8021q_vlan_add(dsa_to_port(ds, port), tx_vid,
-                                          true);
+       dsa_port_tag_8021q_vlan_del(dp, bridge_vid, true);
 }
-EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_tx_fwd_offload);
+EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_leave);
 
-void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,
-                                          struct dsa_bridge bridge)
-{
-       u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
-
-       dsa_port_tag_8021q_vlan_del(dsa_to_port(ds, port), tx_vid, true);
-}
-EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_tx_fwd_unoffload);
-
-/* Set up a port's tag_8021q RX and TX VLAN for standalone mode operation */
+/* Set up a port's standalone tag_8021q VLAN */
 static int dsa_tag_8021q_port_setup(struct dsa_switch *ds, int port)
 {
        struct dsa_8021q_context *ctx = ds->tag_8021q_ctx;
        struct dsa_port *dp = dsa_to_port(ds, port);
-       u16 rx_vid = dsa_tag_8021q_rx_vid(dp);
-       u16 tx_vid = dsa_tag_8021q_tx_vid(dp);
+       u16 vid = dsa_tag_8021q_standalone_vid(dp);
        struct net_device *master;
        int err;
 
@@ -446,30 +342,16 @@ static int dsa_tag_8021q_port_setup(struct dsa_switch *ds, int port)
 
        master = dp->cpu_dp->master;
 
-       /* Add this user port's RX VID to the membership list of all others
-        * (including itself). This is so that bridging will not be hindered.
-        * L2 forwarding rules still take precedence when there are no VLAN
-        * restrictions, so there are no concerns about leaking traffic.
-        */
-       err = dsa_port_tag_8021q_vlan_add(dp, rx_vid, false);
+       err = dsa_port_tag_8021q_vlan_add(dp, vid, false);
        if (err) {
                dev_err(ds->dev,
-                       "Failed to apply RX VID %d to port %d: %pe\n",
-                       rx_vid, port, ERR_PTR(err));
+                       "Failed to apply standalone VID %d to port %d: %pe\n",
+                       vid, port, ERR_PTR(err));
                return err;
        }
 
-       /* Add @rx_vid to the master's RX filter. */
-       vlan_vid_add(master, ctx->proto, rx_vid);
-
-       /* Finally apply the TX VID on this port and on the CPU port */
-       err = dsa_port_tag_8021q_vlan_add(dp, tx_vid, false);
-       if (err) {
-               dev_err(ds->dev,
-                       "Failed to apply TX VID %d on port %d: %pe\n",
-                       tx_vid, port, ERR_PTR(err));
-               return err;
-       }
+       /* Add the VLAN to the master's RX filter. */
+       vlan_vid_add(master, ctx->proto, vid);
 
        return err;
 }
@@ -478,8 +360,7 @@ static void dsa_tag_8021q_port_teardown(struct dsa_switch *ds, int port)
 {
        struct dsa_8021q_context *ctx = ds->tag_8021q_ctx;
        struct dsa_port *dp = dsa_to_port(ds, port);
-       u16 rx_vid = dsa_tag_8021q_rx_vid(dp);
-       u16 tx_vid = dsa_tag_8021q_tx_vid(dp);
+       u16 vid = dsa_tag_8021q_standalone_vid(dp);
        struct net_device *master;
 
        /* The CPU port is implicitly configured by
@@ -490,11 +371,9 @@ static void dsa_tag_8021q_port_teardown(struct dsa_switch *ds, int port)
 
        master = dp->cpu_dp->master;
 
-       dsa_port_tag_8021q_vlan_del(dp, rx_vid, false);
+       dsa_port_tag_8021q_vlan_del(dp, vid, false);
 
-       vlan_vid_del(master, ctx->proto, rx_vid);
-
-       dsa_port_tag_8021q_vlan_del(dp, tx_vid, false);
+       vlan_vid_del(master, ctx->proto, vid);
 }
 
 static int dsa_tag_8021q_setup(struct dsa_switch *ds)
@@ -573,7 +452,37 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
 }
 EXPORT_SYMBOL_GPL(dsa_8021q_xmit);
 
-void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id)
+struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master,
+                                                  int vbid)
+{
+       struct dsa_port *cpu_dp = master->dsa_ptr;
+       struct dsa_switch_tree *dst = cpu_dp->dst;
+       struct dsa_port *dp;
+
+       if (WARN_ON(!vbid))
+               return NULL;
+
+       dsa_tree_for_each_user_port(dp, dst) {
+               if (!dp->bridge)
+                       continue;
+
+               if (dp->stp_state != BR_STATE_LEARNING &&
+                   dp->stp_state != BR_STATE_FORWARDING)
+                       continue;
+
+               if (dp->cpu_dp != cpu_dp)
+                       continue;
+
+               if (dsa_port_bridge_num_get(dp) == vbid)
+                       return dp->slave;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_port_by_vbid);
+
+void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id,
+                  int *vbid)
 {
        u16 vid, tci;
 
@@ -590,6 +499,10 @@ void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id)
 
        *source_port = dsa_8021q_rx_source_port(vid);
        *switch_id = dsa_8021q_rx_switch_id(vid);
+
+       if (vbid)
+               *vbid = dsa_tag_8021q_rx_vbid(vid);
+
        skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
 }
 EXPORT_SYMBOL_GPL(dsa_8021q_rcv);
index bd6f1d0..37ccf00 100644 (file)
@@ -62,7 +62,7 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
        struct dsa_port *dp = dsa_slave_to_port(netdev);
        u16 queue_mapping = skb_get_queue_mapping(skb);
        u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
-       u16 tx_vid = dsa_tag_8021q_tx_vid(dp);
+       u16 tx_vid = dsa_tag_8021q_standalone_vid(dp);
        struct ethhdr *hdr = eth_hdr(skb);
 
        if (ocelot_ptp_rew_op(skb) || is_link_local_ether_addr(hdr->h_dest))
@@ -77,7 +77,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
 {
        int src_port, switch_id;
 
-       dsa_8021q_rcv(skb, &src_port, &switch_id);
+       dsa_8021q_rcv(skb, &src_port, &switch_id, NULL);
 
        skb->dev = dsa_master_find_slave(netdev, switch_id, src_port);
        if (!skb->dev)
index 72d5e0e..83e4136 100644 (file)
@@ -226,7 +226,7 @@ static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb,
         * TX VLAN that targets the bridge's entire broadcast domain,
         * instead of just the specific port.
         */
-       tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge_num);
+       tx_vid = dsa_tag_8021q_bridge_vid(bridge_num);
 
        return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp), tx_vid);
 }
@@ -267,7 +267,7 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
        struct dsa_port *dp = dsa_slave_to_port(netdev);
        u16 queue_mapping = skb_get_queue_mapping(skb);
        u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
-       u16 tx_vid = dsa_tag_8021q_tx_vid(dp);
+       u16 tx_vid = dsa_tag_8021q_standalone_vid(dp);
 
        if (skb->offload_fwd_mark)
                return sja1105_imprecise_xmit(skb, netdev);
@@ -295,7 +295,7 @@ static struct sk_buff *sja1110_xmit(struct sk_buff *skb,
        struct dsa_port *dp = dsa_slave_to_port(netdev);
        u16 queue_mapping = skb_get_queue_mapping(skb);
        u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
-       u16 tx_vid = dsa_tag_8021q_tx_vid(dp);
+       u16 tx_vid = dsa_tag_8021q_standalone_vid(dp);
        __be32 *tx_trailer;
        __be16 *tx_header;
        int trailer_pos;
@@ -509,7 +509,7 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb)
  * packet.
  */
 static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
-                            int *switch_id, u16 *vid)
+                            int *switch_id, int *vbid, u16 *vid)
 {
        struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
        u16 vlan_tci;
@@ -519,8 +519,8 @@ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
        else
                vlan_tci = ntohs(hdr->h_vlan_TCI);
 
-       if (vid_is_dsa_8021q_rxvlan(vlan_tci & VLAN_VID_MASK))
-               return dsa_8021q_rcv(skb, source_port, switch_id);
+       if (vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK))
+               return dsa_8021q_rcv(skb, source_port, switch_id, vbid);
 
        /* Try our best with imprecise RX */
        *vid = vlan_tci & VLAN_VID_MASK;
@@ -529,7 +529,7 @@ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
 static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
                                   struct net_device *netdev)
 {
-       int source_port = -1, switch_id = -1;
+       int source_port = -1, switch_id = -1, vbid = -1;
        struct sja1105_meta meta = {0};
        struct ethhdr *hdr;
        bool is_link_local;
@@ -542,7 +542,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
 
        if (sja1105_skb_has_tag_8021q(skb)) {
                /* Normal traffic path. */
-               sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
+               sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid);
        } else if (is_link_local) {
                /* Management traffic path. Switch embeds the switch ID and
                 * port ID into bytes of the destination MAC, courtesy of
@@ -561,7 +561,9 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
                return NULL;
        }
 
-       if (source_port == -1 || switch_id == -1)
+       if (vbid >= 1)
+               skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid);
+       else if (source_port == -1 || switch_id == -1)
                skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
        else
                skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
@@ -686,7 +688,7 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,
 static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
                                   struct net_device *netdev)
 {
-       int source_port = -1, switch_id = -1;
+       int source_port = -1, switch_id = -1, vbid = -1;
        bool host_only = false;
        u16 vid = 0;
 
@@ -700,9 +702,11 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
 
        /* Packets with in-band control extensions might still have RX VLANs */
        if (likely(sja1105_skb_has_tag_8021q(skb)))
-               sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
+               sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid);
 
-       if (source_port == -1 || switch_id == -1)
+       if (vbid >= 1)
+               skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid);
+       else if (source_port == -1 || switch_id == -1)
                skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
        else
                skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);