From 1dc0408cdf3caf3a8b8ad97c831ae52d2ab5b953 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 1 Oct 2020 19:42:12 -0700 Subject: [PATCH 1/4] net: dsa: Call dsa_untag_bridge_pvid() from dsa_switch_rcv() When a DSA switch driver needs to call dsa_untag_bridge_pvid(), it can set dsa_switch::untag_brige_pvid to indicate this is necessary. This is a pre-requisite to making sure that we are always calling dsa_untag_bridge_pvid() after eth_type_trans() has been called. Signed-off-by: Florian Fainelli Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller --- include/net/dsa.h | 8 ++++++++ net/dsa/dsa.c | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index b502a63d196e..8b0696e08cac 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -308,6 +308,14 @@ struct dsa_switch { */ bool configure_vlan_while_not_filtering; + /* If the switch driver always programs the CPU port as egress tagged + * despite the VLAN configuration indicating otherwise, then setting + * @untag_bridge_pvid will force the DSA receive path to pop the bridge's + * default_pvid VLAN tagged frames to offer a consistent behavior + * between a vlan_filtering=0 and vlan_filtering=1 bridge device. + */ + bool untag_bridge_pvid; + /* In case vlan_filtering_is_global is set, the VLAN awareness state * should be retrieved from here and not from the per-port settings. */ diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 5c18c0214aac..dec4ab59b7c4 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -225,6 +225,15 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, skb->dev); + if (unlikely(cpu_dp->ds->untag_bridge_pvid)) { + nskb = dsa_untag_bridge_pvid(skb); + if (!nskb) { + kfree_skb(skb); + return 0; + } + skb = nskb; + } + s = this_cpu_ptr(p->stats64); u64_stats_update_begin(&s->syncp); s->rx_packets++; From 1c5ad5a940387ce03bc6b6ce497e3523963292bd Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 1 Oct 2020 19:42:13 -0700 Subject: [PATCH 2/4] net: dsa: b53: Set untag_bridge_pvid Indicate to the DSA receive path that we need to untage the bridge PVID, this allows us to remove the dsa_untag_bridge_pvid() calls from net/dsa/tag_brcm.c. Signed-off-by: Florian Fainelli Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 1 + net/dsa/tag_brcm.c | 15 ++------------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 73507cff3bc4..ce18ba0b74eb 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -2603,6 +2603,7 @@ struct b53_device *b53_switch_alloc(struct device *base, dev->ops = ops; ds->ops = &b53_switch_ops; ds->configure_vlan_while_not_filtering = true; + ds->untag_bridge_pvid = true; dev->vlan_enabled = ds->configure_vlan_while_not_filtering; mutex_init(&dev->reg_mutex); mutex_init(&dev->stats_mutex); diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 69d6b8c597a9..ad72dff8d524 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -152,11 +152,6 @@ static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb, /* Remove Broadcom tag and update checksum */ skb_pull_rcsum(skb, BRCM_TAG_LEN); - /* Set the MAC header to where it should point for - * dsa_untag_bridge_pvid() to parse the correct VLAN header. - */ - skb_set_mac_header(skb, -ETH_HLEN); - skb->offload_fwd_mark = 1; return skb; @@ -187,7 +182,7 @@ static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, nskb->data - ETH_HLEN - BRCM_TAG_LEN, 2 * ETH_ALEN); - return dsa_untag_bridge_pvid(nskb); + return nskb; } static const struct dsa_device_ops brcm_netdev_ops = { @@ -214,14 +209,8 @@ static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) { - struct sk_buff *nskb; - /* tag is prepended to the packet */ - nskb = brcm_tag_rcv_ll(skb, dev, pt, ETH_HLEN); - if (!nskb) - return nskb; - - return dsa_untag_bridge_pvid(nskb); + return brcm_tag_rcv_ll(skb, dev, pt, ETH_HLEN); } static const struct dsa_device_ops brcm_prepend_netdev_ops = { From a348292b63156290e72e2df2f724833607d1e403 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 1 Oct 2020 19:42:14 -0700 Subject: [PATCH 3/4] net: dsa: Obtain VLAN protocol from skb->protocol Now that dsa_untag_bridge_pvid() is called after eth_type_trans() we are guaranteed that skb->protocol will be set to a correct value, thus allowing us to avoid calling vlan_eth_hdr(). Signed-off-by: Florian Fainelli Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller --- net/dsa/dsa_priv.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 0348dbab4131..d6ce8c2a2590 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -201,7 +201,6 @@ dsa_slave_to_master(const struct net_device *dev) static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb) { struct dsa_port *dp = dsa_slave_to_port(skb->dev); - struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); struct net_device *br = dp->bridge_dev; struct net_device *dev = skb->dev; struct net_device *upper_dev; @@ -217,7 +216,7 @@ static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb) return skb; /* Move VLAN tag from data to hwaccel */ - if (!skb_vlan_tag_present(skb) && hdr->h_vlan_proto == htons(proto)) { + if (!skb_vlan_tag_present(skb) && skb->protocol == htons(proto)) { skb = skb_vlan_untag(skb); if (!skb) return NULL; From 3a68844dd2d8b3fd23f92894b6628e02a530b445 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 1 Oct 2020 19:42:15 -0700 Subject: [PATCH 4/4] net: dsa: Utilize __vlan_find_dev_deep_rcu() Now that we are guaranteed that dsa_untag_bridge_pvid() is called after eth_type_trans() we can utilize __vlan_find_dev_deep_rcu() which will take care of finding an 802.1Q upper on top of a bridge master. A common use case, prior to 12a1526d067 ("net: dsa: untag the bridge pvid from rx skbs") was to configure a bridge 802.1Q upper like this: ip link add name br0 type bridge vlan_filtering 0 ip link add link br0 name br0.1 type vlan id 1 in order to pop the default_pvid VLAN tag. With this change we restore that behavior while still allowing the DSA receive path to automatically pop the VLAN tag. Signed-off-by: Florian Fainelli Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller --- net/dsa/dsa_priv.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index d6ce8c2a2590..12998bf04e55 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -204,7 +204,6 @@ static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb) struct net_device *br = dp->bridge_dev; struct net_device *dev = skb->dev; struct net_device *upper_dev; - struct list_head *iter; u16 vid, pvid, proto; int err; @@ -246,13 +245,9 @@ static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb) * supports because vlan_filtering is 0. In that case, we should * definitely keep the tag, to make sure it keeps working. */ - netdev_for_each_upper_dev_rcu(dev, upper_dev, iter) { - if (!is_vlan_dev(upper_dev)) - continue; - - if (vid == vlan_dev_vlan_id(upper_dev)) - return skb; - } + upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid); + if (upper_dev) + return skb; __vlan_hwaccel_clear_tag(skb);