Skip to content

Commit

Permalink
net: dsa: tag_8021q: add support for imprecise RX based on the VBID
Browse files Browse the repository at this point in the history
The sja1105 switch can't populate the PORT field of the tag_8021q header
when sending a frame to the CPU with a non-zero VBID.

Similar to dsa_find_designated_bridge_port_by_vid() which performs
imprecise RX for VLAN-aware bridges, let's introduce a helper in
tag_8021q for performing imprecise RX based on the VLAN that it has
allocated for a VLAN-unaware bridge.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed Feb 27, 2022
1 parent 91495f2 commit d7f9787
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 13 deletions.
6 changes: 5 additions & 1 deletion include/linux/dsa/8021q.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port,
struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
u16 tpid, u16 tci);

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

struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master,
int vbid);

u16 dsa_8021q_bridge_tx_fwd_offload_vid(unsigned int bridge_num);

Expand Down
38 changes: 36 additions & 2 deletions net/dsa/tag_8021q.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* VBID - { VID[9], VID[5:4] }:
* Virtual bridge ID. If between 1 and 7, packet targets the broadcast
* domain of a bridge. If transmitted as zero, packet targets a single
* port. Field only valid on transmit, must be ignored on receive.
* port.
*
* PORT - VID[3:0]:
* Index of switch port. Must be between 0 and 15.
Expand Down Expand Up @@ -533,7 +533,37 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
}
EXPORT_SYMBOL_GPL(dsa_8021q_xmit);

void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id)
struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master,
int vbid)
{
struct dsa_port *cpu_dp = master->dsa_ptr;
struct dsa_switch_tree *dst = cpu_dp->dst;
struct dsa_port *dp;

if (WARN_ON(!vbid))
return NULL;

dsa_tree_for_each_user_port(dp, dst) {
if (!dp->bridge)
continue;

if (dp->stp_state != BR_STATE_LEARNING &&
dp->stp_state != BR_STATE_FORWARDING)
continue;

if (dp->cpu_dp != cpu_dp)
continue;

if (dsa_port_bridge_num_get(dp) == vbid)
return dp->slave;
}

return NULL;
}
EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_port_by_vbid);

void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id,
int *vbid)
{
u16 vid, tci;

Expand All @@ -550,6 +580,10 @@ void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id)

*source_port = dsa_8021q_rx_source_port(vid);
*switch_id = dsa_8021q_rx_switch_id(vid);

if (vbid)
*vbid = dsa_tag_8021q_rx_vbid(vid);

skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
}
EXPORT_SYMBOL_GPL(dsa_8021q_rcv);
2 changes: 1 addition & 1 deletion net/dsa/tag_ocelot_8021q.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
{
int src_port, switch_id;

dsa_8021q_rcv(skb, &src_port, &switch_id);
dsa_8021q_rcv(skb, &src_port, &switch_id, NULL);

skb->dev = dsa_master_find_slave(netdev, switch_id, src_port);
if (!skb->dev)
Expand Down
22 changes: 13 additions & 9 deletions net/dsa/tag_sja1105.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb)
* packet.
*/
static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
int *switch_id, u16 *vid)
int *switch_id, int *vbid, u16 *vid)
{
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
u16 vlan_tci;
Expand All @@ -519,8 +519,8 @@ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
else
vlan_tci = ntohs(hdr->h_vlan_TCI);

if (vid_is_dsa_8021q_rxvlan(vlan_tci & VLAN_VID_MASK))
return dsa_8021q_rcv(skb, source_port, switch_id);
if (vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK))
return dsa_8021q_rcv(skb, source_port, switch_id, vbid);

/* Try our best with imprecise RX */
*vid = vlan_tci & VLAN_VID_MASK;
Expand All @@ -529,7 +529,7 @@ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
struct net_device *netdev)
{
int source_port = -1, switch_id = -1;
int source_port = -1, switch_id = -1, vbid = -1;
struct sja1105_meta meta = {0};
struct ethhdr *hdr;
bool is_link_local;
Expand All @@ -542,7 +542,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,

if (sja1105_skb_has_tag_8021q(skb)) {
/* Normal traffic path. */
sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid);
} else if (is_link_local) {
/* Management traffic path. Switch embeds the switch ID and
* port ID into bytes of the destination MAC, courtesy of
Expand All @@ -561,7 +561,9 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
return NULL;
}

if (source_port == -1 || switch_id == -1)
if (vbid >= 1)
skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid);
else if (source_port == -1 || switch_id == -1)
skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
else
skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
Expand Down Expand Up @@ -686,7 +688,7 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,
static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
struct net_device *netdev)
{
int source_port = -1, switch_id = -1;
int source_port = -1, switch_id = -1, vbid = -1;
bool host_only = false;
u16 vid = 0;

Expand All @@ -700,9 +702,11 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,

/* Packets with in-band control extensions might still have RX VLANs */
if (likely(sja1105_skb_has_tag_8021q(skb)))
sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid);

if (source_port == -1 || switch_id == -1)
if (vbid >= 1)
skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid);
else if (source_port == -1 || switch_id == -1)
skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
else
skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
Expand Down

0 comments on commit d7f9787

Please sign in to comment.