Skip to content

Commit 6087175

Browse files
LGA1150davem330
authored andcommitted
net: dsa: mt7530: use independent VLAN learning on VLAN-unaware bridges
Consider the following bridge configuration, where bond0 is not offloaded: +-- br0 --+ / / | \ / / | \ / | | bond0 / | | / \ swp0 swp1 swp2 swp3 swp4 . . . . . . A B C Ideally, when the switch receives a packet from swp3 or swp4, it should forward the packet to the CPU, according to the port matrix and unknown unicast flood settings. But packet loss will happen if the destination address is at one of the offloaded ports (swp0~2). For example, when client C sends a packet to A, the FDB lookup will indicate that it should be forwarded to swp0, but the port matrix of swp3 and swp4 is configured to only allow the CPU to be its destination, so it is dropped. However, this issue does not happen if the bridge is VLAN-aware. That is because VLAN-aware bridges use independent VLAN learning, i.e. use VID for FDB lookup, on offloaded ports. As swp3 and swp4 are not offloaded, shared VLAN learning with default filter ID of 0 is used instead. So the lookup for A with filter ID 0 never hits and the packet can be forwarded to the CPU. In the current code, only two combinations were used to toggle user ports' VLAN awareness: one is PCR.PORT_VLAN set to port matrix mode with PVC.VLAN_ATTR set to transparent port, the other is PCR.PORT_VLAN set to security mode with PVC.VLAN_ATTR set to user port. It turns out that only PVC.VLAN_ATTR contributes to VLAN awareness, and port matrix mode just skips the VLAN table lookup. The reference manual is somehow misleading when describing PORT_VLAN modes. It states that PORT_MEM (VLAN port member) is used for destination if the VLAN table lookup hits, but actually **PORT_MEM & PORT_MATRIX** (bitwise AND of VLAN port member and port matrix) is used instead, which means we can have two or more separate VLAN-aware bridges with the same PVID and traffic won't leak between them. Therefore, to solve this, enable independent VLAN learning with PVID 0 on VLAN-unaware bridges, by setting their PCR.PORT_VLAN to fallback mode, while leaving standalone ports in port matrix mode. The CPU port is always set to fallback mode to serve those bridges. During testing, it is found that FDB lookup with filter ID of 0 will also hit entries with VID 0 even with independent VLAN learning. To avoid that, install all VLANs with filter ID of 1. Signed-off-by: DENG Qingfang <[email protected]> Reviewed-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0b69c54 commit 6087175

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

drivers/net/dsa/mt7530.c

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,10 @@ mt753x_cpu_port_enable(struct dsa_switch *ds, int port)
10211021
mt7530_write(priv, MT7530_PCR_P(port),
10221022
PCR_MATRIX(dsa_user_ports(priv->ds)));
10231023

1024+
/* Set to fallback mode for independent VLAN learning */
1025+
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
1026+
MT7530_PORT_FALLBACK_MODE);
1027+
10241028
return 0;
10251029
}
10261030

@@ -1229,6 +1233,10 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
12291233
PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap));
12301234
priv->ports[port].pm |= PCR_MATRIX(port_bitmap);
12311235

1236+
/* Set to fallback mode for independent VLAN learning */
1237+
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
1238+
MT7530_PORT_FALLBACK_MODE);
1239+
12321240
mutex_unlock(&priv->reg_mutex);
12331241

12341242
return 0;
@@ -1241,16 +1249,21 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
12411249
bool all_user_ports_removed = true;
12421250
int i;
12431251

1244-
/* When a port is removed from the bridge, the port would be set up
1245-
* back to the default as is at initial boot which is a VLAN-unaware
1246-
* port.
1252+
/* This is called after .port_bridge_leave when leaving a VLAN-aware
1253+
* bridge. Don't set standalone ports to fallback mode.
12471254
*/
1248-
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
1249-
MT7530_PORT_MATRIX_MODE);
1255+
if (dsa_to_port(ds, port)->bridge_dev)
1256+
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
1257+
MT7530_PORT_FALLBACK_MODE);
1258+
12501259
mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK,
12511260
VLAN_ATTR(MT7530_VLAN_TRANSPARENT) |
12521261
PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
12531262

1263+
/* Set PVID to 0 */
1264+
mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
1265+
G0_PORT_VID_DEF);
1266+
12541267
for (i = 0; i < MT7530_NUM_PORTS; i++) {
12551268
if (dsa_is_user_port(ds, i) &&
12561269
dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
@@ -1276,15 +1289,14 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port)
12761289
struct mt7530_priv *priv = ds->priv;
12771290

12781291
/* Trapped into security mode allows packet forwarding through VLAN
1279-
* table lookup. CPU port is set to fallback mode to let untagged
1280-
* frames pass through.
1292+
* table lookup.
12811293
*/
1282-
if (dsa_is_cpu_port(ds, port))
1283-
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
1284-
MT7530_PORT_FALLBACK_MODE);
1285-
else
1294+
if (dsa_is_user_port(ds, port)) {
12861295
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
12871296
MT7530_PORT_SECURITY_MODE);
1297+
mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
1298+
G0_PORT_VID(priv->ports[port].pvid));
1299+
}
12881300

12891301
/* Set the port as a user port which is to be able to recognize VID
12901302
* from incoming packets before fetching entry within the VLAN table.
@@ -1329,6 +1341,13 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
13291341
PCR_MATRIX(BIT(MT7530_CPU_PORT)));
13301342
priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));
13311343

1344+
/* When a port is removed from the bridge, the port would be set up
1345+
* back to the default as is at initial boot which is a VLAN-unaware
1346+
* port.
1347+
*/
1348+
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
1349+
MT7530_PORT_MATRIX_MODE);
1350+
13321351
mutex_unlock(&priv->reg_mutex);
13331352
}
13341353

@@ -1511,7 +1530,8 @@ mt7530_hw_vlan_add(struct mt7530_priv *priv,
15111530
/* Validate the entry with independent learning, create egress tag per
15121531
* VLAN and joining the port as one of the port members.
15131532
*/
1514-
val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | VLAN_VALID;
1533+
val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | FID(FID_BRIDGED) |
1534+
VLAN_VALID;
15151535
mt7530_write(priv, MT7530_VAWD1, val);
15161536

15171537
/* Decide whether adding tag or not for those outgoing packets from the
@@ -1601,9 +1621,13 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port,
16011621
mt7530_hw_vlan_update(priv, vlan->vid, &new_entry, mt7530_hw_vlan_add);
16021622

16031623
if (pvid) {
1604-
mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
1605-
G0_PORT_VID(vlan->vid));
16061624
priv->ports[port].pvid = vlan->vid;
1625+
1626+
/* Only configure PVID if VLAN filtering is enabled */
1627+
if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port)))
1628+
mt7530_rmw(priv, MT7530_PPBV1_P(port),
1629+
G0_PORT_VID_MASK,
1630+
G0_PORT_VID(vlan->vid));
16071631
}
16081632

16091633
mutex_unlock(&priv->reg_mutex);
@@ -1617,23 +1641,22 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,
16171641
{
16181642
struct mt7530_hw_vlan_entry target_entry;
16191643
struct mt7530_priv *priv = ds->priv;
1620-
u16 pvid;
16211644

16221645
mutex_lock(&priv->reg_mutex);
16231646

1624-
pvid = priv->ports[port].pvid;
16251647
mt7530_hw_vlan_entry_init(&target_entry, port, 0);
16261648
mt7530_hw_vlan_update(priv, vlan->vid, &target_entry,
16271649
mt7530_hw_vlan_del);
16281650

16291651
/* PVID is being restored to the default whenever the PVID port
16301652
* is being removed from the VLAN.
16311653
*/
1632-
if (pvid == vlan->vid)
1633-
pvid = G0_PORT_VID_DEF;
1654+
if (priv->ports[port].pvid == vlan->vid) {
1655+
priv->ports[port].pvid = G0_PORT_VID_DEF;
1656+
mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
1657+
G0_PORT_VID_DEF);
1658+
}
16341659

1635-
mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid);
1636-
priv->ports[port].pvid = pvid;
16371660

16381661
mutex_unlock(&priv->reg_mutex);
16391662

@@ -2126,6 +2149,10 @@ mt7530_setup(struct dsa_switch *ds)
21262149
return ret;
21272150
} else {
21282151
mt7530_port_disable(ds, i);
2152+
2153+
/* Set default PVID to 0 on all user ports */
2154+
mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
2155+
G0_PORT_VID_DEF);
21292156
}
21302157
/* Enable consistent egress tag */
21312158
mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK,
@@ -2293,6 +2320,10 @@ mt7531_setup(struct dsa_switch *ds)
22932320
return ret;
22942321
} else {
22952322
mt7530_port_disable(ds, i);
2323+
2324+
/* Set default PVID to 0 on all user ports */
2325+
mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
2326+
G0_PORT_VID_DEF);
22962327
}
22972328

22982329
/* Enable consistent egress tag */

drivers/net/dsa/mt7530.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,18 @@ enum mt7530_vlan_cmd {
148148
#define VTAG_EN BIT(28)
149149
/* VLAN Member Control */
150150
#define PORT_MEM(x) (((x) & 0xff) << 16)
151+
/* Filter ID */
152+
#define FID(x) (((x) & 0x7) << 1)
151153
/* VLAN Entry Valid */
152154
#define VLAN_VALID BIT(0)
153155
#define PORT_MEM_SHFT 16
154156
#define PORT_MEM_MASK 0xff
155157

158+
enum mt7530_fid {
159+
FID_STANDALONE = 0,
160+
FID_BRIDGED = 1,
161+
};
162+
156163
#define MT7530_VAWD2 0x98
157164
/* Egress Tag Control */
158165
#define ETAG_CTRL_P(p, x) (((x) & 0x3) << ((p) << 1))
@@ -247,7 +254,7 @@ enum mt7530_vlan_port_attr {
247254
#define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100))
248255
#define G0_PORT_VID(x) (((x) & 0xfff) << 0)
249256
#define G0_PORT_VID_MASK G0_PORT_VID(0xfff)
250-
#define G0_PORT_VID_DEF G0_PORT_VID(1)
257+
#define G0_PORT_VID_DEF G0_PORT_VID(0)
251258

252259
/* Register for port MAC control register */
253260
#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100))

0 commit comments

Comments
 (0)