Skip to content

Commit 8b6836d

Browse files
vladimirolteankuba-moo
authored andcommitted
net: dsa: mv88e6xxx: keep the pvid at 0 when VLAN-unaware
The VLAN support in mv88e6xxx has a loaded history. Commit 2ea7a67 ("net: dsa: Don't add vlans when vlan filtering is disabled") noticed some issues with VLAN and decided the best way to deal with them was to make the DSA core ignore VLANs added by the bridge while VLAN awareness is turned off. Those issues were never explained, just presented as "at least one corner case". That approach had problems of its own, presented by commit 54a0ed0 ("net: dsa: provide an option for drivers to always receive bridge VLANs") for the DSA core, followed by commit 1fb7419 ("net: dsa: mv88e6xxx: fix vlan setup") which applied ds->configure_vlan_while_not_filtering = true for mv88e6xxx in particular. We still don't know what corner case Andrew saw when he wrote commit 2ea7a67 ("net: dsa: Don't add vlans when vlan filtering is disabled"), but Tobias now reports that when we use TX forwarding offload, pinging an external station from the bridge device is broken if the front-facing DSA user port has flooding turned off. The full description is in the link below, but for short, when a mv88e6xxx port is under a VLAN-unaware bridge, it inherits that bridge's pvid. So packets ingressing a user port will be classified to e.g. VID 1 (assuming that value for the bridge_default_pvid), whereas when tag_dsa.c xmits towards a user port, it always sends packets using a VID of 0 if that port is standalone or under a VLAN-unaware bridge - or at least it did so prior to commit d82f8ab ("net: dsa: tag_dsa: offload the bridge forwarding process"). In any case, when there is a conversation between the CPU and a station connected to a user port, the station's MAC address is learned in VID 1 but the CPU tries to transmit through VID 0. The packets reach the intended station, but via flooding and not by virtue of matching the existing ATU entry. DSA has established (and enforced in other drivers: sja1105, felix, mt7530) that a VLAN-unaware port should use a private pvid, and not inherit the one from the bridge. The bridge's pvid should only be inherited when that bridge is VLAN-aware, so all state transitions need to be handled. On the other hand, all bridge VLANs should sit in the VTU starting with the moment when the bridge offloads them via switchdev, they are just not used. This solves the problem that Tobias sees because packets ingressing on VLAN-unaware user ports now get classified to VID 0, which is also the VID used by tag_dsa.c on xmit. Fixes: d82f8ab ("net: dsa: tag_dsa: offload the bridge forwarding process") Link: https://patchwork.kernel.org/project/netdevbpf/patch/[email protected]/#24491503 Reported-by: Tobias Waldekranz <[email protected]> Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent c7709a0 commit 8b6836d

File tree

4 files changed

+76
-6
lines changed

4 files changed

+76
-6
lines changed

drivers/net/dsa/mv88e6xxx/chip.c

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,6 +1677,26 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
16771677
return 0;
16781678
}
16791679

1680+
static int mv88e6xxx_port_commit_pvid(struct mv88e6xxx_chip *chip, int port)
1681+
{
1682+
struct dsa_port *dp = dsa_to_port(chip->ds, port);
1683+
struct mv88e6xxx_port *p = &chip->ports[port];
1684+
bool drop_untagged = false;
1685+
u16 pvid = 0;
1686+
int err;
1687+
1688+
if (dp->bridge_dev && br_vlan_enabled(dp->bridge_dev)) {
1689+
pvid = p->bridge_pvid.vid;
1690+
drop_untagged = !p->bridge_pvid.valid;
1691+
}
1692+
1693+
err = mv88e6xxx_port_set_pvid(chip, port, pvid);
1694+
if (err)
1695+
return err;
1696+
1697+
return mv88e6xxx_port_drop_untagged(chip, port, drop_untagged);
1698+
}
1699+
16801700
static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
16811701
bool vlan_filtering,
16821702
struct netlink_ext_ack *extack)
@@ -1690,7 +1710,16 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
16901710
return -EOPNOTSUPP;
16911711

16921712
mv88e6xxx_reg_lock(chip);
1713+
16931714
err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
1715+
if (err)
1716+
goto unlock;
1717+
1718+
err = mv88e6xxx_port_commit_pvid(chip, port);
1719+
if (err)
1720+
goto unlock;
1721+
1722+
unlock:
16941723
mv88e6xxx_reg_unlock(chip);
16951724

16961725
return err;
@@ -2123,6 +2152,7 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
21232152
struct mv88e6xxx_chip *chip = ds->priv;
21242153
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
21252154
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2155+
struct mv88e6xxx_port *p = &chip->ports[port];
21262156
bool warn;
21272157
u8 member;
21282158
int err;
@@ -2156,13 +2186,21 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
21562186
}
21572187

21582188
if (pvid) {
2159-
err = mv88e6xxx_port_set_pvid(chip, port, vlan->vid);
2160-
if (err) {
2161-
dev_err(ds->dev, "p%d: failed to set PVID %d\n",
2162-
port, vlan->vid);
2189+
p->bridge_pvid.vid = vlan->vid;
2190+
p->bridge_pvid.valid = true;
2191+
2192+
err = mv88e6xxx_port_commit_pvid(chip, port);
2193+
if (err)
2194+
goto out;
2195+
} else if (vlan->vid && p->bridge_pvid.vid == vlan->vid) {
2196+
/* The old pvid was reinstalled as a non-pvid VLAN */
2197+
p->bridge_pvid.valid = false;
2198+
2199+
err = mv88e6xxx_port_commit_pvid(chip, port);
2200+
if (err)
21632201
goto out;
2164-
}
21652202
}
2203+
21662204
out:
21672205
mv88e6xxx_reg_unlock(chip);
21682206

@@ -2212,6 +2250,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
22122250
const struct switchdev_obj_port_vlan *vlan)
22132251
{
22142252
struct mv88e6xxx_chip *chip = ds->priv;
2253+
struct mv88e6xxx_port *p = &chip->ports[port];
22152254
int err = 0;
22162255
u16 pvid;
22172256

@@ -2229,7 +2268,9 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
22292268
goto unlock;
22302269

22312270
if (vlan->vid == pvid) {
2232-
err = mv88e6xxx_port_set_pvid(chip, port, 0);
2271+
p->bridge_pvid.valid = false;
2272+
2273+
err = mv88e6xxx_port_commit_pvid(chip, port);
22332274
if (err)
22342275
goto unlock;
22352276
}

drivers/net/dsa/mv88e6xxx/chip.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,15 @@ struct mv88e6xxx_policy {
246246
u16 vid;
247247
};
248248

249+
struct mv88e6xxx_vlan {
250+
u16 vid;
251+
bool valid;
252+
};
253+
249254
struct mv88e6xxx_port {
250255
struct mv88e6xxx_chip *chip;
251256
int port;
257+
struct mv88e6xxx_vlan bridge_pvid;
252258
u64 serdes_stats[2];
253259
u64 atu_member_violation;
254260
u64 atu_miss_violation;

drivers/net/dsa/mv88e6xxx/port.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,27 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
12571257
return 0;
12581258
}
12591259

1260+
int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
1261+
bool drop_untagged)
1262+
{
1263+
u16 old, new;
1264+
int err;
1265+
1266+
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &old);
1267+
if (err)
1268+
return err;
1269+
1270+
if (drop_untagged)
1271+
new = old | MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
1272+
else
1273+
new = old & ~MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
1274+
1275+
if (new == old)
1276+
return 0;
1277+
1278+
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new);
1279+
}
1280+
12601281
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
12611282
{
12621283
u16 reg;

drivers/net/dsa/mv88e6xxx/port.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
423423
phy_interface_t mode);
424424
int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
425425
int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
426+
int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
427+
bool drop_untagged);
426428
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
427429
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
428430
int upstream_port);

0 commit comments

Comments
 (0)