Commit bea79078 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: dsa: don't set skb->offload_fwd_mark when not offloading the bridge

DSA has gained the recent ability to deal gracefully with upper
interfaces it cannot offload, such as the bridge, bonding or team
drivers. When such uppers exist, the ports are still in standalone mode
as far as the hardware is concerned.

But when we deliver packets to the software bridge in order for that to
do the forwarding, there is an unpleasant surprise in that the bridge
will refuse to forward them. This is because we unconditionally set
skb->offload_fwd_mark = true, meaning that the bridge thinks the frames
were already forwarded in hardware by us.

Since dp->bridge_dev is populated only when there is hardware offload
for it, but not in the software fallback case, let's introduce a new
helper that can be called from the tagger data path which sets the
skb->offload_fwd_mark accordingly to zero when there is no hardware
offload for bridging. This lets the bridge forward packets back to other
interfaces of our switch, if needed.
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: default avatarTobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 57fb346c
...@@ -440,6 +440,20 @@ dsa_find_designated_bridge_port_by_vid(struct net_device *master, u16 vid) ...@@ -440,6 +440,20 @@ dsa_find_designated_bridge_port_by_vid(struct net_device *master, u16 vid)
return NULL; return NULL;
} }
/* If the ingress port offloads the bridge, we mark the frame as autonomously
* forwarded by hardware, so the software bridge doesn't forward in twice, back
* to us, because we already did. However, if we're in fallback mode and we do
* software bridging, we are not offloading it, therefore the dp->bridge_dev
* pointer is not populated, and flooding needs to be done by software (we are
* effectively operating in standalone ports mode).
*/
static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb)
{
struct dsa_port *dp = dsa_slave_to_port(skb->dev);
skb->offload_fwd_mark = !!(dp->bridge_dev);
}
/* switch.c */ /* switch.c */
int dsa_switch_register_notifier(struct dsa_switch *ds); int dsa_switch_register_notifier(struct dsa_switch *ds);
void dsa_switch_unregister_notifier(struct dsa_switch *ds); void dsa_switch_unregister_notifier(struct dsa_switch *ds);
......
...@@ -167,7 +167,7 @@ static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb, ...@@ -167,7 +167,7 @@ static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb,
/* Remove Broadcom tag and update checksum */ /* Remove Broadcom tag and update checksum */
skb_pull_rcsum(skb, BRCM_TAG_LEN); skb_pull_rcsum(skb, BRCM_TAG_LEN);
skb->offload_fwd_mark = 1; dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
...@@ -271,7 +271,7 @@ static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, ...@@ -271,7 +271,7 @@ static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb,
/* Remove Broadcom tag and update checksum */ /* Remove Broadcom tag and update checksum */
skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN); skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN);
skb->offload_fwd_mark = 1; dsa_default_offload_fwd_mark(skb);
/* Move the Ethernet DA and SA */ /* Move the Ethernet DA and SA */
memmove(skb->data - ETH_HLEN, memmove(skb->data - ETH_HLEN,
......
...@@ -198,8 +198,8 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev, ...@@ -198,8 +198,8 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev,
static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
u8 extra) u8 extra)
{ {
bool trap = false, trunk = false;
int source_device, source_port; int source_device, source_port;
bool trunk = false;
enum dsa_code code; enum dsa_code code;
enum dsa_cmd cmd; enum dsa_cmd cmd;
u8 *dsa_header; u8 *dsa_header;
...@@ -210,8 +210,6 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, ...@@ -210,8 +210,6 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
cmd = dsa_header[0] >> 6; cmd = dsa_header[0] >> 6;
switch (cmd) { switch (cmd) {
case DSA_CMD_FORWARD: case DSA_CMD_FORWARD:
skb->offload_fwd_mark = 1;
trunk = !!(dsa_header[1] & 7); trunk = !!(dsa_header[1] & 7);
break; break;
...@@ -230,7 +228,6 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, ...@@ -230,7 +228,6 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
* device (like a bridge) that forwarding has * device (like a bridge) that forwarding has
* already been done by hardware. * already been done by hardware.
*/ */
skb->offload_fwd_mark = 1;
break; break;
case DSA_CODE_MGMT_TRAP: case DSA_CODE_MGMT_TRAP:
case DSA_CODE_IGMP_MLD_TRAP: case DSA_CODE_IGMP_MLD_TRAP:
...@@ -238,6 +235,7 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, ...@@ -238,6 +235,7 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
/* Traps have, by definition, not been /* Traps have, by definition, not been
* forwarded by hardware, so don't mark them. * forwarded by hardware, so don't mark them.
*/ */
trap = true;
break; break;
default: default:
/* Reserved code, this could be anything. Drop /* Reserved code, this could be anything. Drop
...@@ -271,6 +269,15 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, ...@@ -271,6 +269,15 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
if (!skb->dev) if (!skb->dev)
return NULL; return NULL;
/* When using LAG offload, skb->dev is not a DSA slave interface,
* so we cannot call dsa_default_offload_fwd_mark and we need to
* special-case it.
*/
if (trunk)
skb->offload_fwd_mark = true;
else if (!trap)
dsa_default_offload_fwd_mark(skb);
/* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q /* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q
* tag, and delete the ethertype (extra) if applicable. If the * tag, and delete the ethertype (extra) if applicable. If the
* 'tagged' bit is cleared; delete the DSA tag, and ethertype * 'tagged' bit is cleared; delete the DSA tag, and ethertype
......
...@@ -44,7 +44,7 @@ static struct sk_buff *hellcreek_rcv(struct sk_buff *skb, ...@@ -44,7 +44,7 @@ static struct sk_buff *hellcreek_rcv(struct sk_buff *skb,
pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN); pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN);
skb->offload_fwd_mark = true; dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
...@@ -24,7 +24,7 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb, ...@@ -24,7 +24,7 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,
pskb_trim_rcsum(skb, skb->len - len); pskb_trim_rcsum(skb, skb->len - len);
skb->offload_fwd_mark = true; dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
...@@ -115,7 +115,8 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -115,7 +115,8 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev,
skb_pull_rcsum(skb, 2 + 2); skb_pull_rcsum(skb, 2 + 2);
memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN), memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN),
2 * ETH_ALEN); 2 * ETH_ALEN);
skb->offload_fwd_mark = !(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU); if (!(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU))
dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
...@@ -92,7 +92,7 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -92,7 +92,7 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev,
if (!skb->dev) if (!skb->dev)
return NULL; return NULL;
skb->offload_fwd_mark = 1; dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
...@@ -104,7 +104,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ...@@ -104,7 +104,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
*/ */
return NULL; return NULL;
skb->offload_fwd_mark = 1; dsa_default_offload_fwd_mark(skb);
skb->priority = qos_class; skb->priority = qos_class;
/* Ocelot switches copy frames unmodified to the CPU. However, it is /* Ocelot switches copy frames unmodified to the CPU. However, it is
......
...@@ -49,7 +49,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ...@@ -49,7 +49,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
if (!skb->dev) if (!skb->dev)
return NULL; return NULL;
skb->offload_fwd_mark = 1; dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
...@@ -114,7 +114,7 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, ...@@ -114,7 +114,7 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb,
skb->data - ETH_HLEN - RTL4_A_HDR_LEN, skb->data - ETH_HLEN - RTL4_A_HDR_LEN,
2 * ETH_ALEN); 2 * ETH_ALEN);
skb->offload_fwd_mark = 1; dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
...@@ -405,8 +405,6 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -405,8 +405,6 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
is_link_local = sja1105_is_link_local(skb); is_link_local = sja1105_is_link_local(skb);
is_meta = sja1105_is_meta_frame(skb); is_meta = sja1105_is_meta_frame(skb);
skb->offload_fwd_mark = 1;
if (sja1105_skb_has_tag_8021q(skb)) { if (sja1105_skb_has_tag_8021q(skb)) {
/* Normal traffic path. */ /* Normal traffic path. */
sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid); sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
...@@ -437,6 +435,9 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -437,6 +435,9 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
return NULL; return NULL;
} }
if (!is_link_local)
dsa_default_offload_fwd_mark(skb);
return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local, return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local,
is_meta); is_meta);
} }
...@@ -480,7 +481,8 @@ static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header) ...@@ -480,7 +481,8 @@ static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header)
static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,
int *source_port, int *source_port,
int *switch_id) int *switch_id,
bool *host_only)
{ {
u16 rx_header; u16 rx_header;
...@@ -494,6 +496,9 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, ...@@ -494,6 +496,9 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,
*/ */
rx_header = ntohs(*(__be16 *)skb->data); rx_header = ntohs(*(__be16 *)skb->data);
if (rx_header & SJA1110_RX_HEADER_HOST_ONLY)
*host_only = true;
if (rx_header & SJA1110_RX_HEADER_IS_METADATA) if (rx_header & SJA1110_RX_HEADER_IS_METADATA)
return sja1110_rcv_meta(skb, rx_header); return sja1110_rcv_meta(skb, rx_header);
...@@ -545,13 +550,13 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, ...@@ -545,13 +550,13 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
struct packet_type *pt) struct packet_type *pt)
{ {
int source_port = -1, switch_id = -1; int source_port = -1, switch_id = -1;
bool host_only = false;
u16 vid; u16 vid;
skb->offload_fwd_mark = 1;
if (sja1110_skb_has_inband_control_extension(skb)) { if (sja1110_skb_has_inband_control_extension(skb)) {
skb = sja1110_rcv_inband_control_extension(skb, &source_port, skb = sja1110_rcv_inband_control_extension(skb, &source_port,
&switch_id); &switch_id,
&host_only);
if (!skb) if (!skb)
return NULL; return NULL;
} }
...@@ -569,6 +574,9 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, ...@@ -569,6 +574,9 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
return NULL; return NULL;
} }
if (!host_only)
dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
...@@ -46,7 +46,7 @@ static struct sk_buff *xrs700x_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -46,7 +46,7 @@ static struct sk_buff *xrs700x_rcv(struct sk_buff *skb, struct net_device *dev,
return NULL; return NULL;
/* Frame is forwarded by hardware, don't forward in software. */ /* Frame is forwarded by hardware, don't forward in software. */
skb->offload_fwd_mark = 1; dsa_default_offload_fwd_mark(skb);
return skb; return skb;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment