Skip to content

Commit 91495f2

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: tag_8021q: replace the SVL bridging with VLAN-unaware IVL bridging
For VLAN-unaware bridging, tag_8021q uses something perhaps a bit too tied with the sja1105 switch: each port uses the same pvid which is also used for standalone operation (a unique one from which the source port and device ID can be retrieved when packets from that port are forwarded to the CPU). Since each port has a unique pvid when performing autonomous forwarding, the switch must be configured for Shared VLAN Learning (SVL) such that the VLAN ID itself is ignored when performing FDB lookups. Without SVL, packets would always be flooded, since FDB lookup in the source port's VLAN would never find any entry. First of all, to make tag_8021q more palatable to switches which might not support Shared VLAN Learning, let's just use a common VLAN for all ports that are under the same bridge. Secondly, using Shared VLAN Learning means that FDB isolation can never be enforced. But if all ports under the same VLAN-unaware bridge share the same VLAN ID, it can. The disadvantage is that the CPU port can no longer perform precise source port identification for these packets. But at least we have a mechanism which has proven to be adequate for that situation: imprecise RX (dsa_find_designated_bridge_port_by_vid), which is what we use for termination on VLAN-aware bridges. The VLAN ID that VLAN-unaware bridges will use with tag_8021q is the same one as we were previously using for imprecise TX (bridge TX forwarding offload). It is already allocated, it is just a matter of using it. Note that because now all ports under the same bridge share the same VLAN, the complexity of performing a tag_8021q bridge join decreases dramatically. We no longer have to install the RX VLAN of a newly joining port into the port membership of the existing bridge ports. The newly joining port just becomes a member of the VLAN corresponding to that bridge, and the other ports are already members of it from when they joined the bridge themselves. So forwarding works properly. This means that we can unhook dsa_tag_8021q_bridge_{join,leave} from the cross-chip notifier level dsa_switch_bridge_{join,leave}. We can put these calls directly into the sja1105 driver. With this new mode of operation, a port controlled by tag_8021q can have two pvids whereas before it could only have one. The pvid for standalone operation is different from the pvid used for VLAN-unaware bridging. This is done, again, so that FDB isolation can be enforced. Let tag_8021q manage this by deleting the standalone pvid when a port joins a bridge, and restoring it when it leaves it. Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 1bb1c5b commit 91495f2

File tree

6 files changed

+70
-101
lines changed

6 files changed

+70
-101
lines changed

drivers/net/dsa/sja1105/sja1105_main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,7 +2067,7 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port,
20672067
if (rc)
20682068
return rc;
20692069

2070-
rc = dsa_tag_8021q_bridge_tx_fwd_offload(ds, port, bridge);
2070+
rc = dsa_tag_8021q_bridge_join(ds, port, bridge);
20712071
if (rc) {
20722072
sja1105_bridge_member(ds, port, bridge, false);
20732073
return rc;
@@ -2081,7 +2081,7 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port,
20812081
static void sja1105_bridge_leave(struct dsa_switch *ds, int port,
20822082
struct dsa_bridge bridge)
20832083
{
2084-
dsa_tag_8021q_bridge_tx_fwd_unoffload(ds, port, bridge);
2084+
dsa_tag_8021q_bridge_leave(ds, port, bridge);
20852085
sja1105_bridge_member(ds, port, bridge, false);
20862086
}
20872087

drivers/net/dsa/sja1105/sja1105_vl.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,19 @@ static bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a,
296296
return false;
297297
}
298298

299+
/* FIXME: this should change when the bridge upper of the port changes. */
300+
static u16 sja1105_port_get_tag_8021q_vid(struct dsa_port *dp)
301+
{
302+
unsigned long bridge_num;
303+
304+
if (!dp->bridge)
305+
return dsa_tag_8021q_rx_vid(dp);
306+
307+
bridge_num = dsa_port_bridge_num_get(dp);
308+
309+
return dsa_8021q_bridge_tx_fwd_offload_vid(bridge_num);
310+
}
311+
299312
static int sja1105_init_virtual_links(struct sja1105_private *priv,
300313
struct netlink_ext_ack *extack)
301314
{
@@ -395,7 +408,7 @@ static int sja1105_init_virtual_links(struct sja1105_private *priv,
395408
vl_lookup[k].vlanprior = rule->key.vl.pcp;
396409
} else {
397410
struct dsa_port *dp = dsa_to_port(priv->ds, port);
398-
u16 vid = dsa_tag_8021q_rx_vid(dp);
411+
u16 vid = sja1105_port_get_tag_8021q_vid(dp);
399412

400413
vl_lookup[k].vlanid = vid;
401414
vl_lookup[k].vlanprior = 0;

include/linux/dsa/8021q.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,17 @@ int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto);
3232

3333
void dsa_tag_8021q_unregister(struct dsa_switch *ds);
3434

35+
int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
36+
struct dsa_bridge bridge);
37+
38+
void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port,
39+
struct dsa_bridge bridge);
40+
3541
struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
3642
u16 tpid, u16 tci);
3743

3844
void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id);
3945

40-
int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
41-
struct dsa_bridge bridge);
42-
43-
void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,
44-
struct dsa_bridge bridge);
45-
4646
u16 dsa_8021q_bridge_tx_fwd_offload_vid(unsigned int bridge_num);
4747

4848
u16 dsa_tag_8021q_tx_vid(const struct dsa_port *dp);

net/dsa/dsa_priv.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -522,10 +522,6 @@ struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst,
522522
const struct net_device *br);
523523

524524
/* tag_8021q.c */
525-
int dsa_tag_8021q_bridge_join(struct dsa_switch *ds,
526-
struct dsa_notifier_bridge_info *info);
527-
int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds,
528-
struct dsa_notifier_bridge_info *info);
529525
int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds,
530526
struct dsa_notifier_tag_8021q_vlan_info *info);
531527
int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds,

net/dsa/switch.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds,
110110
return err;
111111
}
112112

113-
return dsa_tag_8021q_bridge_join(ds, info);
113+
return 0;
114114
}
115115

116116
static int dsa_switch_sync_vlan_filtering(struct dsa_switch *ds,
@@ -186,7 +186,7 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
186186
return err;
187187
}
188188

189-
return dsa_tag_8021q_bridge_leave(ds, info);
189+
return 0;
190190
}
191191

192192
/* Matches for all upstream-facing ports (the CPU port and all upstream-facing

net/dsa/tag_8021q.c

Lines changed: 46 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ int dsa_8021q_rx_source_port(u16 vid)
110110
}
111111
EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port);
112112

113+
/* Returns the decoded VBID from the RX VID. */
114+
static int dsa_tag_8021q_rx_vbid(u16 vid)
115+
{
116+
u16 vbid_hi = (vid & DSA_8021Q_VBID_HI_MASK) >> DSA_8021Q_VBID_HI_SHIFT;
117+
u16 vbid_lo = (vid & DSA_8021Q_VBID_LO_MASK) >> DSA_8021Q_VBID_LO_SHIFT;
118+
119+
return (vbid_hi << 2) | vbid_lo;
120+
}
121+
113122
bool vid_is_dsa_8021q_rxvlan(u16 vid)
114123
{
115124
return (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_RX;
@@ -244,11 +253,17 @@ int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds,
244253
if (dsa_port_is_user(dp))
245254
flags |= BRIDGE_VLAN_INFO_UNTAGGED;
246255

256+
/* Standalone VLANs are PVIDs */
247257
if (vid_is_dsa_8021q_rxvlan(info->vid) &&
248258
dsa_8021q_rx_switch_id(info->vid) == ds->index &&
249259
dsa_8021q_rx_source_port(info->vid) == dp->index)
250260
flags |= BRIDGE_VLAN_INFO_PVID;
251261

262+
/* And bridging VLANs are PVIDs too on user ports */
263+
if (dsa_tag_8021q_rx_vbid(info->vid) &&
264+
dsa_port_is_user(dp))
265+
flags |= BRIDGE_VLAN_INFO_PVID;
266+
252267
err = dsa_port_do_tag_8021q_vlan_add(dp, info->vid,
253268
flags);
254269
if (err)
@@ -326,107 +341,52 @@ int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds,
326341
* +-+-----+-+-----+-+-----+-+-----+-+ +-+-----+-+-----+-+-----+-+-----+-+
327342
* swp0 swp1 swp2 swp3 swp0 swp1 swp2 swp3
328343
*/
329-
static bool
330-
dsa_port_tag_8021q_bridge_match(struct dsa_port *dp,
331-
struct dsa_notifier_bridge_info *info)
344+
int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
345+
struct dsa_bridge bridge)
332346
{
333-
/* Don't match on self */
334-
if (dp->ds->dst->index == info->tree_index &&
335-
dp->ds->index == info->sw_index &&
336-
dp->index == info->port)
337-
return false;
338-
339-
if (dsa_port_is_user(dp))
340-
return dsa_port_offloads_bridge(dp, &info->bridge);
341-
342-
return false;
343-
}
344-
345-
int dsa_tag_8021q_bridge_join(struct dsa_switch *ds,
346-
struct dsa_notifier_bridge_info *info)
347-
{
348-
struct dsa_switch *targeted_ds;
349-
struct dsa_port *targeted_dp;
350-
struct dsa_port *dp;
351-
u16 targeted_rx_vid;
347+
struct dsa_port *dp = dsa_to_port(ds, port);
348+
u16 standalone_vid, bridge_vid;
352349
int err;
353350

354-
if (!ds->tag_8021q_ctx)
355-
return 0;
356-
357-
targeted_ds = dsa_switch_find(info->tree_index, info->sw_index);
358-
targeted_dp = dsa_to_port(targeted_ds, info->port);
359-
targeted_rx_vid = dsa_tag_8021q_rx_vid(targeted_dp);
360-
361-
dsa_switch_for_each_port(dp, ds) {
362-
u16 rx_vid = dsa_tag_8021q_rx_vid(dp);
363-
364-
if (!dsa_port_tag_8021q_bridge_match(dp, info))
365-
continue;
351+
/* Delete the standalone VLAN of the port and replace it with a
352+
* bridging VLAN
353+
*/
354+
standalone_vid = dsa_tag_8021q_rx_vid(dp);
355+
bridge_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
366356

367-
/* Install the RX VID of the targeted port in our VLAN table */
368-
err = dsa_port_tag_8021q_vlan_add(dp, targeted_rx_vid, true);
369-
if (err)
370-
return err;
357+
err = dsa_port_tag_8021q_vlan_add(dp, bridge_vid, true);
358+
if (err)
359+
return err;
371360

372-
/* Install our RX VID into the targeted port's VLAN table */
373-
err = dsa_port_tag_8021q_vlan_add(targeted_dp, rx_vid, true);
374-
if (err)
375-
return err;
376-
}
361+
dsa_port_tag_8021q_vlan_del(dp, standalone_vid, false);
377362

378363
return 0;
379364
}
365+
EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_join);
380366

381-
int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds,
382-
struct dsa_notifier_bridge_info *info)
367+
void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port,
368+
struct dsa_bridge bridge)
383369
{
384-
struct dsa_switch *targeted_ds;
385-
struct dsa_port *targeted_dp;
386-
struct dsa_port *dp;
387-
u16 targeted_rx_vid;
388-
389-
if (!ds->tag_8021q_ctx)
390-
return 0;
391-
392-
targeted_ds = dsa_switch_find(info->tree_index, info->sw_index);
393-
targeted_dp = dsa_to_port(targeted_ds, info->port);
394-
targeted_rx_vid = dsa_tag_8021q_rx_vid(targeted_dp);
395-
396-
dsa_switch_for_each_port(dp, ds) {
397-
u16 rx_vid = dsa_tag_8021q_rx_vid(dp);
398-
399-
if (!dsa_port_tag_8021q_bridge_match(dp, info))
400-
continue;
370+
struct dsa_port *dp = dsa_to_port(ds, port);
371+
u16 standalone_vid, bridge_vid;
372+
int err;
401373

402-
/* Remove the RX VID of the targeted port from our VLAN table */
403-
dsa_port_tag_8021q_vlan_del(dp, targeted_rx_vid, true);
374+
/* Delete the bridging VLAN of the port and replace it with a
375+
* standalone VLAN
376+
*/
377+
standalone_vid = dsa_tag_8021q_rx_vid(dp);
378+
bridge_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
404379

405-
/* Remove our RX VID from the targeted port's VLAN table */
406-
dsa_port_tag_8021q_vlan_del(targeted_dp, rx_vid, true);
380+
err = dsa_port_tag_8021q_vlan_add(dp, standalone_vid, false);
381+
if (err) {
382+
dev_err(ds->dev,
383+
"Failed to delete tag_8021q standalone VLAN %d from port %d: %pe\n",
384+
standalone_vid, port, ERR_PTR(err));
407385
}
408386

409-
return 0;
410-
}
411-
412-
int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
413-
struct dsa_bridge bridge)
414-
{
415-
u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
416-
417-
return dsa_port_tag_8021q_vlan_add(dsa_to_port(ds, port), tx_vid,
418-
true);
419-
}
420-
EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_tx_fwd_offload);
421-
422-
void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,
423-
struct dsa_bridge bridge)
424-
{
425-
u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
426-
427-
dsa_port_tag_8021q_vlan_del(dsa_to_port(ds, port), tx_vid, true);
387+
dsa_port_tag_8021q_vlan_del(dp, bridge_vid, true);
428388
}
429-
EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_tx_fwd_unoffload);
389+
EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_leave);
430390

431391
/* Set up a port's tag_8021q RX and TX VLAN for standalone mode operation */
432392
static int dsa_tag_8021q_port_setup(struct dsa_switch *ds, int port)

0 commit comments

Comments
 (0)