f9fe0ef858ae6dda93acc77c3e622f47a8abb115
[openwrt/staging/dedeckeh.git] /
1 From e3a402764c5753698e7a9e45d4d21f093faa7852 Mon Sep 17 00:00:00 2001
2 From: DENG Qingfang <dqfext@gmail.com>
3 Date: Wed, 4 Aug 2021 00:04:02 +0800
4 Subject: [PATCH] net: dsa: mt7530: use independent VLAN learning on
5 VLAN-unaware bridges
6
7 Consider the following bridge configuration, where bond0 is not
8 offloaded:
9
10 +-- br0 --+
11 / / | \
12 / / | \
13 / | | bond0
14 / | | / \
15 swp0 swp1 swp2 swp3 swp4
16 . . .
17 . . .
18 A B C
19
20 Ideally, when the switch receives a packet from swp3 or swp4, it should
21 forward the packet to the CPU, according to the port matrix and unknown
22 unicast flood settings.
23
24 But packet loss will happen if the destination address is at one of the
25 offloaded ports (swp0~2). For example, when client C sends a packet to
26 A, the FDB lookup will indicate that it should be forwarded to swp0, but
27 the port matrix of swp3 and swp4 is configured to only allow the CPU to
28 be its destination, so it is dropped.
29
30 However, this issue does not happen if the bridge is VLAN-aware. That is
31 because VLAN-aware bridges use independent VLAN learning, i.e. use VID
32 for FDB lookup, on offloaded ports. As swp3 and swp4 are not offloaded,
33 shared VLAN learning with default filter ID of 0 is used instead. So the
34 lookup for A with filter ID 0 never hits and the packet can be forwarded
35 to the CPU.
36
37 In the current code, only two combinations were used to toggle user
38 ports' VLAN awareness: one is PCR.PORT_VLAN set to port matrix mode with
39 PVC.VLAN_ATTR set to transparent port, the other is PCR.PORT_VLAN set to
40 security mode with PVC.VLAN_ATTR set to user port.
41
42 It turns out that only PVC.VLAN_ATTR contributes to VLAN awareness, and
43 port matrix mode just skips the VLAN table lookup. The reference manual
44 is somehow misleading when describing PORT_VLAN modes. It states that
45 PORT_MEM (VLAN port member) is used for destination if the VLAN table
46 lookup hits, but actually **PORT_MEM & PORT_MATRIX** (bitwise AND of
47 VLAN port member and port matrix) is used instead, which means we can
48 have two or more separate VLAN-aware bridges with the same PVID and
49 traffic won't leak between them.
50
51 Therefore, to solve this, enable independent VLAN learning with PVID 0
52 on VLAN-unaware bridges, by setting their PCR.PORT_VLAN to fallback
53 mode, while leaving standalone ports in port matrix mode. The CPU port
54 is always set to fallback mode to serve those bridges.
55
56 During testing, it is found that FDB lookup with filter ID of 0 will
57 also hit entries with VID 0 even with independent VLAN learning. To
58 avoid that, install all VLANs with filter ID of 1.
59
60 Signed-off-by: DENG Qingfang <dqfext@gmail.com>
61 Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
62 Signed-off-by: David S. Miller <davem@davemloft.net>
63 ---
64 drivers/net/dsa/mt7530.c | 72 +++++++++++++++++++++++++++++-----------
65 drivers/net/dsa/mt7530.h | 9 ++++-
66 2 files changed, 60 insertions(+), 21 deletions(-)
67
68 --- a/drivers/net/dsa/mt7530.c
69 +++ b/drivers/net/dsa/mt7530.c
70 @@ -1011,6 +1011,10 @@ mt753x_cpu_port_enable(struct dsa_switch
71 mt7530_write(priv, MT7530_PCR_P(port),
72 PCR_MATRIX(dsa_user_ports(priv->ds)));
73
74 + /* Set to fallback mode for independent VLAN learning */
75 + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
76 + MT7530_PORT_FALLBACK_MODE);
77 +
78 return 0;
79 }
80
81 @@ -1165,6 +1169,10 @@ mt7530_port_bridge_join(struct dsa_switc
82
83 mt7530_clear(priv, MT7530_PSC_P(port), SA_DIS);
84
85 + /* Set to fallback mode for independent VLAN learning */
86 + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
87 + MT7530_PORT_FALLBACK_MODE);
88 +
89 mutex_unlock(&priv->reg_mutex);
90
91 return 0;
92 @@ -1177,16 +1185,21 @@ mt7530_port_set_vlan_unaware(struct dsa_
93 bool all_user_ports_removed = true;
94 int i;
95
96 - /* When a port is removed from the bridge, the port would be set up
97 - * back to the default as is at initial boot which is a VLAN-unaware
98 - * port.
99 + /* This is called after .port_bridge_leave when leaving a VLAN-aware
100 + * bridge. Don't set standalone ports to fallback mode.
101 */
102 - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
103 - MT7530_PORT_MATRIX_MODE);
104 + if (dsa_to_port(ds, port)->bridge_dev)
105 + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
106 + MT7530_PORT_FALLBACK_MODE);
107 +
108 mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK,
109 VLAN_ATTR(MT7530_VLAN_TRANSPARENT) |
110 PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
111
112 + /* Set PVID to 0 */
113 + mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
114 + G0_PORT_VID_DEF);
115 +
116 for (i = 0; i < MT7530_NUM_PORTS; i++) {
117 if (dsa_is_user_port(ds, i) &&
118 dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
119 @@ -1212,15 +1225,14 @@ mt7530_port_set_vlan_aware(struct dsa_sw
120 struct mt7530_priv *priv = ds->priv;
121
122 /* Trapped into security mode allows packet forwarding through VLAN
123 - * table lookup. CPU port is set to fallback mode to let untagged
124 - * frames pass through.
125 + * table lookup.
126 */
127 - if (dsa_is_cpu_port(ds, port))
128 - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
129 - MT7530_PORT_FALLBACK_MODE);
130 - else
131 + if (dsa_is_user_port(ds, port)) {
132 mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
133 MT7530_PORT_SECURITY_MODE);
134 + mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
135 + G0_PORT_VID(priv->ports[port].pvid));
136 + }
137
138 /* Set the port as a user port which is to be able to recognize VID
139 * from incoming packets before fetching entry within the VLAN table.
140 @@ -1264,6 +1276,13 @@ mt7530_port_bridge_leave(struct dsa_swit
141
142 mt7530_set(priv, MT7530_PSC_P(port), SA_DIS);
143
144 + /* When a port is removed from the bridge, the port would be set up
145 + * back to the default as is at initial boot which is a VLAN-unaware
146 + * port.
147 + */
148 + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
149 + MT7530_PORT_MATRIX_MODE);
150 +
151 mutex_unlock(&priv->reg_mutex);
152 }
153
154 @@ -1406,7 +1425,8 @@ mt7530_hw_vlan_add(struct mt7530_priv *p
155 /* Validate the entry with independent learning, create egress tag per
156 * VLAN and joining the port as one of the port members.
157 */
158 - val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | VLAN_VALID;
159 + val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | FID(FID_BRIDGED) |
160 + VLAN_VALID;
161 mt7530_write(priv, MT7530_VAWD1, val);
162
163 /* Decide whether adding tag or not for those outgoing packets from the
164 @@ -1499,9 +1519,13 @@ mt7530_port_vlan_add(struct dsa_switch *
165 }
166
167 if (pvid) {
168 - mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
169 - G0_PORT_VID(vlan->vid_end));
170 priv->ports[port].pvid = vlan->vid_end;
171 +
172 + /* Only configure PVID if VLAN filtering is enabled */
173 + if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port)))
174 + mt7530_rmw(priv, MT7530_PPBV1_P(port),
175 + G0_PORT_VID_MASK,
176 + G0_PORT_VID(vlan->vid_end));
177 }
178
179 mutex_unlock(&priv->reg_mutex);
180 @@ -1513,11 +1537,10 @@ mt7530_port_vlan_del(struct dsa_switch *
181 {
182 struct mt7530_hw_vlan_entry target_entry;
183 struct mt7530_priv *priv = ds->priv;
184 - u16 vid, pvid;
185 + u16 vid;
186
187 mutex_lock(&priv->reg_mutex);
188
189 - pvid = priv->ports[port].pvid;
190 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
191 mt7530_hw_vlan_entry_init(&target_entry, port, 0);
192 mt7530_hw_vlan_update(priv, vid, &target_entry,
193 @@ -1526,12 +1549,13 @@ mt7530_port_vlan_del(struct dsa_switch *
194 /* PVID is being restored to the default whenever the PVID port
195 * is being removed from the VLAN.
196 */
197 - if (pvid == vid)
198 - pvid = G0_PORT_VID_DEF;
199 + if (priv->ports[port].pvid == vid) {
200 + priv->ports[port].pvid = G0_PORT_VID_DEF;
201 + mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
202 + G0_PORT_VID_DEF);
203 + }
204 }
205
206 - mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid);
207 - priv->ports[port].pvid = pvid;
208
209 mutex_unlock(&priv->reg_mutex);
210
211 @@ -1827,6 +1851,10 @@ mt7530_setup(struct dsa_switch *ds)
212 return ret;
213 } else {
214 mt7530_port_disable(ds, i);
215 +
216 + /* Set default PVID to 0 on all user ports */
217 + mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
218 + G0_PORT_VID_DEF);
219 }
220
221 /* Enable consistent egress tag */
222 @@ -1993,6 +2021,10 @@ mt7531_setup(struct dsa_switch *ds)
223 return ret;
224 } else {
225 mt7530_port_disable(ds, i);
226 +
227 + /* Set default PVID to 0 on all user ports */
228 + mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
229 + G0_PORT_VID_DEF);
230 }
231
232 /* Enable consistent egress tag */
233 --- a/drivers/net/dsa/mt7530.h
234 +++ b/drivers/net/dsa/mt7530.h
235 @@ -145,11 +145,18 @@ enum mt7530_vlan_cmd {
236 #define VTAG_EN BIT(28)
237 /* VLAN Member Control */
238 #define PORT_MEM(x) (((x) & 0xff) << 16)
239 +/* Filter ID */
240 +#define FID(x) (((x) & 0x7) << 1)
241 /* VLAN Entry Valid */
242 #define VLAN_VALID BIT(0)
243 #define PORT_MEM_SHFT 16
244 #define PORT_MEM_MASK 0xff
245
246 +enum mt7530_fid {
247 + FID_STANDALONE = 0,
248 + FID_BRIDGED = 1,
249 +};
250 +
251 #define MT7530_VAWD2 0x98
252 /* Egress Tag Control */
253 #define ETAG_CTRL_P(p, x) (((x) & 0x3) << ((p) << 1))
254 @@ -244,7 +251,7 @@ enum mt7530_vlan_port_attr {
255 #define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100))
256 #define G0_PORT_VID(x) (((x) & 0xfff) << 0)
257 #define G0_PORT_VID_MASK G0_PORT_VID(0xfff)
258 -#define G0_PORT_VID_DEF G0_PORT_VID(1)
259 +#define G0_PORT_VID_DEF G0_PORT_VID(0)
260
261 /* Register for port MAC control register */
262 #define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100))