Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vladimir Oltean and committed by
David S. Miller
bea79078 57fb346c

+50 -20
+14
net/dsa/dsa_priv.h
··· 440 440 return NULL; 441 441 } 442 442 443 + /* If the ingress port offloads the bridge, we mark the frame as autonomously 444 + * forwarded by hardware, so the software bridge doesn't forward in twice, back 445 + * to us, because we already did. However, if we're in fallback mode and we do 446 + * software bridging, we are not offloading it, therefore the dp->bridge_dev 447 + * pointer is not populated, and flooding needs to be done by software (we are 448 + * effectively operating in standalone ports mode). 449 + */ 450 + static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb) 451 + { 452 + struct dsa_port *dp = dsa_slave_to_port(skb->dev); 453 + 454 + skb->offload_fwd_mark = !!(dp->bridge_dev); 455 + } 456 + 443 457 /* switch.c */ 444 458 int dsa_switch_register_notifier(struct dsa_switch *ds); 445 459 void dsa_switch_unregister_notifier(struct dsa_switch *ds);
+2 -2
net/dsa/tag_brcm.c
··· 167 167 /* Remove Broadcom tag and update checksum */ 168 168 skb_pull_rcsum(skb, BRCM_TAG_LEN); 169 169 170 - skb->offload_fwd_mark = 1; 170 + dsa_default_offload_fwd_mark(skb); 171 171 172 172 return skb; 173 173 } ··· 271 271 /* Remove Broadcom tag and update checksum */ 272 272 skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN); 273 273 274 - skb->offload_fwd_mark = 1; 274 + dsa_default_offload_fwd_mark(skb); 275 275 276 276 /* Move the Ethernet DA and SA */ 277 277 memmove(skb->data - ETH_HLEN,
+11 -4
net/dsa/tag_dsa.c
··· 198 198 static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, 199 199 u8 extra) 200 200 { 201 + bool trap = false, trunk = false; 201 202 int source_device, source_port; 202 - bool trunk = false; 203 203 enum dsa_code code; 204 204 enum dsa_cmd cmd; 205 205 u8 *dsa_header; ··· 210 210 cmd = dsa_header[0] >> 6; 211 211 switch (cmd) { 212 212 case DSA_CMD_FORWARD: 213 - skb->offload_fwd_mark = 1; 214 - 215 213 trunk = !!(dsa_header[1] & 7); 216 214 break; 217 215 ··· 228 230 * device (like a bridge) that forwarding has 229 231 * already been done by hardware. 230 232 */ 231 - skb->offload_fwd_mark = 1; 232 233 break; 233 234 case DSA_CODE_MGMT_TRAP: 234 235 case DSA_CODE_IGMP_MLD_TRAP: ··· 235 238 /* Traps have, by definition, not been 236 239 * forwarded by hardware, so don't mark them. 237 240 */ 241 + trap = true; 238 242 break; 239 243 default: 240 244 /* Reserved code, this could be anything. Drop ··· 268 270 269 271 if (!skb->dev) 270 272 return NULL; 273 + 274 + /* When using LAG offload, skb->dev is not a DSA slave interface, 275 + * so we cannot call dsa_default_offload_fwd_mark and we need to 276 + * special-case it. 277 + */ 278 + if (trunk) 279 + skb->offload_fwd_mark = true; 280 + else if (!trap) 281 + dsa_default_offload_fwd_mark(skb); 271 282 272 283 /* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q 273 284 * tag, and delete the ethertype (extra) if applicable. If the
+1 -1
net/dsa/tag_hellcreek.c
··· 44 44 45 45 pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN); 46 46 47 - skb->offload_fwd_mark = true; 47 + dsa_default_offload_fwd_mark(skb); 48 48 49 49 return skb; 50 50 }
+1 -1
net/dsa/tag_ksz.c
··· 24 24 25 25 pskb_trim_rcsum(skb, skb->len - len); 26 26 27 - skb->offload_fwd_mark = true; 27 + dsa_default_offload_fwd_mark(skb); 28 28 29 29 return skb; 30 30 }
+2 -1
net/dsa/tag_lan9303.c
··· 115 115 skb_pull_rcsum(skb, 2 + 2); 116 116 memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN), 117 117 2 * ETH_ALEN); 118 - skb->offload_fwd_mark = !(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU); 118 + if (!(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU)) 119 + dsa_default_offload_fwd_mark(skb); 119 120 120 121 return skb; 121 122 }
+1 -1
net/dsa/tag_mtk.c
··· 92 92 if (!skb->dev) 93 93 return NULL; 94 94 95 - skb->offload_fwd_mark = 1; 95 + dsa_default_offload_fwd_mark(skb); 96 96 97 97 return skb; 98 98 }
+1 -1
net/dsa/tag_ocelot.c
··· 104 104 */ 105 105 return NULL; 106 106 107 - skb->offload_fwd_mark = 1; 107 + dsa_default_offload_fwd_mark(skb); 108 108 skb->priority = qos_class; 109 109 110 110 /* Ocelot switches copy frames unmodified to the CPU. However, it is
+1 -1
net/dsa/tag_ocelot_8021q.c
··· 49 49 if (!skb->dev) 50 50 return NULL; 51 51 52 - skb->offload_fwd_mark = 1; 52 + dsa_default_offload_fwd_mark(skb); 53 53 54 54 return skb; 55 55 }
+1 -1
net/dsa/tag_rtl4_a.c
··· 114 114 skb->data - ETH_HLEN - RTL4_A_HDR_LEN, 115 115 2 * ETH_ALEN); 116 116 117 - skb->offload_fwd_mark = 1; 117 + dsa_default_offload_fwd_mark(skb); 118 118 119 119 return skb; 120 120 }
+14 -6
net/dsa/tag_sja1105.c
··· 405 405 is_link_local = sja1105_is_link_local(skb); 406 406 is_meta = sja1105_is_meta_frame(skb); 407 407 408 - skb->offload_fwd_mark = 1; 409 - 410 408 if (sja1105_skb_has_tag_8021q(skb)) { 411 409 /* Normal traffic path. */ 412 410 sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid); ··· 434 436 netdev_warn(netdev, "Couldn't decode source port\n"); 435 437 return NULL; 436 438 } 439 + 440 + if (!is_link_local) 441 + dsa_default_offload_fwd_mark(skb); 437 442 438 443 return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local, 439 444 is_meta); ··· 481 480 482 481 static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, 483 482 int *source_port, 484 - int *switch_id) 483 + int *switch_id, 484 + bool *host_only) 485 485 { 486 486 u16 rx_header; 487 487 ··· 495 493 * comes afterwards. 496 494 */ 497 495 rx_header = ntohs(*(__be16 *)skb->data); 496 + 497 + if (rx_header & SJA1110_RX_HEADER_HOST_ONLY) 498 + *host_only = true; 498 499 499 500 if (rx_header & SJA1110_RX_HEADER_IS_METADATA) 500 501 return sja1110_rcv_meta(skb, rx_header); ··· 550 545 struct packet_type *pt) 551 546 { 552 547 int source_port = -1, switch_id = -1; 548 + bool host_only = false; 553 549 u16 vid; 554 - 555 - skb->offload_fwd_mark = 1; 556 550 557 551 if (sja1110_skb_has_inband_control_extension(skb)) { 558 552 skb = sja1110_rcv_inband_control_extension(skb, &source_port, 559 - &switch_id); 553 + &switch_id, 554 + &host_only); 560 555 if (!skb) 561 556 return NULL; 562 557 } ··· 573 568 netdev_warn(netdev, "Couldn't decode source port\n"); 574 569 return NULL; 575 570 } 571 + 572 + if (!host_only) 573 + dsa_default_offload_fwd_mark(skb); 576 574 577 575 return skb; 578 576 }
+1 -1
net/dsa/tag_xrs700x.c
··· 46 46 return NULL; 47 47 48 48 /* Frame is forwarded by hardware, don't forward in software. */ 49 - skb->offload_fwd_mark = 1; 49 + dsa_default_offload_fwd_mark(skb); 50 50 51 51 return skb; 52 52 }