Skip to content

Commit 5f33183

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: tag_8021q: Restore bridge VLANs when enabling vlan_filtering
The bridge core assumes that enabling/disabling vlan_filtering will translate into the simple toggling of a flag for switchdev drivers. That is clearly not the case for sja1105, which alters the VLAN table and the pvids in order to obtain port separation in standalone mode. There are 2 parts to the issue. First, tag_8021q changes the pvid to a unique per-port rx_vid for frame identification. But we need to disable tag_8021q when vlan_filtering kicks in, and at that point, the VLAN configured as pvid will have to be removed from the filtering table of the ports. With an invalid pvid, the ports will drop all traffic. Since the bridge will not call any vlan operation through switchdev after enabling vlan_filtering, we need to ensure we're in a functional state ourselves. Hence read the pvid that the bridge is aware of, and program that into our ports. Secondly, tag_8021q uses the 1024-3071 range privately in vlan_filtering=0 mode. Had the user installed one of these VLANs during a previous vlan_filtering=1 session, then upon the next tag_8021q cleanup for vlan_filtering to kick in again, VLANs in that range will get deleted unconditionally, hence breaking user expectation. So when deleting the VLANs, check if the bridge had knowledge about them, and if it did, re-apply the settings. Wrap this logic inside a dsa_8021q_vid_apply helper function to reduce code duplication. Signed-off-by: Vladimir Oltean <[email protected]> Reviewed-by: Vivien Didelot <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f40d9b2 commit 5f33183

File tree

1 file changed

+82
-20
lines changed

1 file changed

+82
-20
lines changed

net/dsa/tag_8021q.c

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,79 @@ int dsa_8021q_rx_source_port(u16 vid)
9191
}
9292
EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port);
9393

94+
static int dsa_8021q_restore_pvid(struct dsa_switch *ds, int port)
95+
{
96+
struct bridge_vlan_info vinfo;
97+
struct net_device *slave;
98+
u16 pvid;
99+
int err;
100+
101+
if (!dsa_is_user_port(ds, port))
102+
return 0;
103+
104+
slave = ds->ports[port].slave;
105+
106+
err = br_vlan_get_pvid(slave, &pvid);
107+
if (err < 0)
108+
/* There is no pvid on the bridge for this port, which is
109+
* perfectly valid. Nothing to restore, bye-bye!
110+
*/
111+
return 0;
112+
113+
err = br_vlan_get_info(slave, pvid, &vinfo);
114+
if (err < 0) {
115+
dev_err(ds->dev, "Couldn't determine PVID attributes\n");
116+
return err;
117+
}
118+
119+
return dsa_port_vid_add(&ds->ports[port], pvid, vinfo.flags);
120+
}
121+
122+
/* If @enabled is true, installs @vid with @flags into the switch port's HW
123+
* filter.
124+
* If @enabled is false, deletes @vid (ignores @flags) from the port. Had the
125+
* user explicitly configured this @vid through the bridge core, then the @vid
126+
* is installed again, but this time with the flags from the bridge layer.
127+
*/
128+
static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid,
129+
u16 flags, bool enabled)
130+
{
131+
struct dsa_port *dp = &ds->ports[port];
132+
struct bridge_vlan_info vinfo;
133+
int err;
134+
135+
if (enabled)
136+
return dsa_port_vid_add(dp, vid, flags);
137+
138+
err = dsa_port_vid_del(dp, vid);
139+
if (err < 0)
140+
return err;
141+
142+
/* Nothing to restore from the bridge for a non-user port.
143+
* The CPU port VLANs are restored implicitly with the user ports,
144+
* similar to how the bridge does in dsa_slave_vlan_add and
145+
* dsa_slave_vlan_del.
146+
*/
147+
if (!dsa_is_user_port(ds, port))
148+
return 0;
149+
150+
err = br_vlan_get_info(dp->slave, vid, &vinfo);
151+
/* Couldn't determine bridge attributes for this vid,
152+
* it means the bridge had not configured it.
153+
*/
154+
if (err < 0)
155+
return 0;
156+
157+
/* Restore the VID from the bridge */
158+
err = dsa_port_vid_add(dp, vid, vinfo.flags);
159+
if (err < 0)
160+
return err;
161+
162+
vinfo.flags &= ~BRIDGE_VLAN_INFO_PVID;
163+
164+
return dsa_port_vid_add(dp->cpu_dp, vid, vinfo.flags);
165+
}
166+
94167
/* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single
95168
* front-panel switch port (here swp0).
96169
*
@@ -146,8 +219,6 @@ EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port);
146219
int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
147220
{
148221
int upstream = dsa_upstream_port(ds, port);
149-
struct dsa_port *dp = &ds->ports[port];
150-
struct dsa_port *upstream_dp = &ds->ports[upstream];
151222
u16 rx_vid = dsa_8021q_rx_vid(ds, port);
152223
u16 tx_vid = dsa_8021q_tx_vid(ds, port);
153224
int i, err;
@@ -164,7 +235,6 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
164235
* restrictions, so there are no concerns about leaking traffic.
165236
*/
166237
for (i = 0; i < ds->num_ports; i++) {
167-
struct dsa_port *other_dp = &ds->ports[i];
168238
u16 flags;
169239

170240
if (i == upstream)
@@ -177,10 +247,7 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
177247
/* The RX VID is a regular VLAN on all others */
178248
flags = BRIDGE_VLAN_INFO_UNTAGGED;
179249

180-
if (enabled)
181-
err = dsa_port_vid_add(other_dp, rx_vid, flags);
182-
else
183-
err = dsa_port_vid_del(other_dp, rx_vid);
250+
err = dsa_8021q_vid_apply(ds, i, rx_vid, flags, enabled);
184251
if (err) {
185252
dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n",
186253
rx_vid, port, err);
@@ -191,37 +258,32 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
191258
/* CPU port needs to see this port's RX VID
192259
* as tagged egress.
193260
*/
194-
if (enabled)
195-
err = dsa_port_vid_add(upstream_dp, rx_vid, 0);
196-
else
197-
err = dsa_port_vid_del(upstream_dp, rx_vid);
261+
err = dsa_8021q_vid_apply(ds, upstream, rx_vid, 0, enabled);
198262
if (err) {
199263
dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n",
200264
rx_vid, port, err);
201265
return err;
202266
}
203267

204268
/* Finally apply the TX VID on this port and on the CPU port */
205-
if (enabled)
206-
err = dsa_port_vid_add(dp, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED);
207-
else
208-
err = dsa_port_vid_del(dp, tx_vid);
269+
err = dsa_8021q_vid_apply(ds, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED,
270+
enabled);
209271
if (err) {
210272
dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n",
211273
tx_vid, port, err);
212274
return err;
213275
}
214-
if (enabled)
215-
err = dsa_port_vid_add(upstream_dp, tx_vid, 0);
216-
else
217-
err = dsa_port_vid_del(upstream_dp, tx_vid);
276+
err = dsa_8021q_vid_apply(ds, upstream, tx_vid, 0, enabled);
218277
if (err) {
219278
dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n",
220279
tx_vid, upstream, err);
221280
return err;
222281
}
223282

224-
return 0;
283+
if (!enabled)
284+
err = dsa_8021q_restore_pvid(ds, port);
285+
286+
return err;
225287
}
226288
EXPORT_SYMBOL_GPL(dsa_port_setup_8021q_tagging);
227289

0 commit comments

Comments
 (0)