aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVivien Didelot <vivien.didelot@savoirfairelinux.com>2015-10-11 18:08:38 -0400
committerDavid S. Miller <davem@davemloft.net>2015-10-13 07:26:31 -0400
commit5fe7f68016ff9dcb59632071f9abf30296bbad3c (patch)
treefe2d3cf9b665b6d6caaa5d3068fd2d5e509ace27
parentefd29b3d8266761570fd3f440e2d5aa24c678725 (diff)
net: dsa: mv88e6xxx: fix hardware bridging
Playing with the VLAN map of every port to implement "hardware bridging" in the 88E6352 driver was a hack until full 802.1Q was supported. Indeed with 802.1Q port mode "Disabled" or "Fallback", this feature is used to restrict which output ports an input port can egress frames to. A Linux bridge is an untagged VLAN. With full 802.1Q support, we don't need this hack anymore and can use the "Secure" strict 802.1Q port mode. With this mode, the port-based VLAN map still needs to be configured, but all the logic is VTU-centric. This means that the switch only cares about rules described in its hardware VLAN table, which is exactly what Linux bridge expects and what we want. Note also that the hardware bridging was broken with the previous flexible "Fallback" 802.1Q port mode. Here's an example: Port0 and Port1 belong to the same bridge. If Port0 sends crafted tagged frames with VID 200 to Port1, Port1 receives it. Even if Port1 is in hardware VLAN 200, but not Port0, Port1 will still receive it, because Fallback mode doesn't care about invalid VID or non-member source port. Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/dsa/mv88e6171.c2
-rw-r--r--drivers/net/dsa/mv88e6352.c2
-rw-r--r--drivers/net/dsa/mv88e6xxx.c47
-rw-r--r--drivers/net/dsa/mv88e6xxx.h2
4 files changed, 3 insertions, 50 deletions
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c
index ca3330aec740..dfca352e78e3 100644
--- a/drivers/net/dsa/mv88e6171.c
+++ b/drivers/net/dsa/mv88e6171.c
@@ -113,8 +113,6 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
113#endif 113#endif
114 .get_regs_len = mv88e6xxx_get_regs_len, 114 .get_regs_len = mv88e6xxx_get_regs_len,
115 .get_regs = mv88e6xxx_get_regs, 115 .get_regs = mv88e6xxx_get_regs,
116 .port_join_bridge = mv88e6xxx_join_bridge,
117 .port_leave_bridge = mv88e6xxx_leave_bridge,
118 .port_stp_update = mv88e6xxx_port_stp_update, 116 .port_stp_update = mv88e6xxx_port_stp_update,
119 .port_pvid_get = mv88e6xxx_port_pvid_get, 117 .port_pvid_get = mv88e6xxx_port_pvid_get,
120 .port_pvid_set = mv88e6xxx_port_pvid_set, 118 .port_pvid_set = mv88e6xxx_port_pvid_set,
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index 078a358c1b83..796fdcbe3c6e 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -340,8 +340,6 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
340 .set_eeprom = mv88e6352_set_eeprom, 340 .set_eeprom = mv88e6352_set_eeprom,
341 .get_regs_len = mv88e6xxx_get_regs_len, 341 .get_regs_len = mv88e6xxx_get_regs_len,
342 .get_regs = mv88e6xxx_get_regs, 342 .get_regs = mv88e6xxx_get_regs,
343 .port_join_bridge = mv88e6xxx_join_bridge,
344 .port_leave_bridge = mv88e6xxx_leave_bridge,
345 .port_stp_update = mv88e6xxx_port_stp_update, 343 .port_stp_update = mv88e6xxx_port_stp_update,
346 .port_pvid_get = mv88e6xxx_port_pvid_get, 344 .port_pvid_get = mv88e6xxx_port_pvid_get,
347 .port_pvid_set = mv88e6xxx_port_pvid_set, 345 .port_pvid_set = mv88e6xxx_port_pvid_set,
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index b2b99f8ef29b..4591240eb795 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -1124,41 +1124,6 @@ static int _mv88e6xxx_port_vlan_map_set(struct dsa_switch *ds, int port,
1124 return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg); 1124 return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
1125} 1125}
1126 1126
1127/* Bridge handling functions */
1128
1129static int mv88e6xxx_map_bridge(struct dsa_switch *ds, u16 members)
1130{
1131 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1132 const unsigned long output = members | BIT(dsa_upstream_port(ds));
1133 int port, err = 0;
1134
1135 mutex_lock(&ps->smi_mutex);
1136
1137 for_each_set_bit(port, &output, ps->num_ports) {
1138 if (dsa_is_cpu_port(ds, port))
1139 continue;
1140
1141 err = _mv88e6xxx_port_vlan_map_set(ds, port, output & ~port);
1142 if (err)
1143 break;
1144 }
1145
1146 mutex_unlock(&ps->smi_mutex);
1147
1148 return err;
1149}
1150
1151
1152int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
1153{
1154 return mv88e6xxx_map_bridge(ds, br_port_mask);
1155}
1156
1157int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
1158{
1159 return mv88e6xxx_map_bridge(ds, br_port_mask & ~port);
1160}
1161
1162int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) 1127int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
1163{ 1128{
1164 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 1129 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
@@ -2007,7 +1972,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
2007 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN; 1972 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2008 } 1973 }
2009 1974
2010 reg |= PORT_CONTROL_2_8021Q_FALLBACK; 1975 reg |= PORT_CONTROL_2_8021Q_SECURE;
2011 1976
2012 if (reg) { 1977 if (reg) {
2013 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 1978 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
@@ -2101,15 +2066,9 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
2101 goto abort; 2066 goto abort;
2102 2067
2103 /* Port based VLAN map: do not give each port its own address 2068 /* Port based VLAN map: do not give each port its own address
2104 * database, allow the CPU port to talk to each of the 'real' 2069 * database, and allow every port to egress frames on all other ports.
2105 * ports, and allow each of the 'real' ports to only talk to
2106 * the upstream port.
2107 */ 2070 */
2108 if (dsa_is_cpu_port(ds, port)) 2071 reg = BIT(ps->num_ports) - 1; /* all ports */
2109 reg = BIT(ps->num_ports) - 1;
2110 else
2111 reg = BIT(dsa_upstream_port(ds));
2112
2113 ret = _mv88e6xxx_port_vlan_map_set(ds, port, reg & ~port); 2072 ret = _mv88e6xxx_port_vlan_map_set(ds, port, reg & ~port);
2114 if (ret) 2073 if (ret)
2115 goto abort; 2074 goto abort;
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 9b6104b94ce4..4ba69f339bfe 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -458,8 +458,6 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
458int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); 458int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
459int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, 459int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
460 struct phy_device *phydev, struct ethtool_eee *e); 460 struct phy_device *phydev, struct ethtool_eee *e);
461int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
462int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
463int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); 461int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
464int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid); 462int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *vid);
465int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 vid); 463int mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 vid);