Hi David, Jakub,
This patch series is based on the recent discussions with Vladimir:
https://lore.kernel.org/netdev/[email protected]/
the simplest way forward was to call dsa_untag_bridge_pvid() after
eth_type_trans() has been set which guarantees that skb->protocol is set
to a correct value and this allows us to utilize
__vlan_find_dev_deep_rcu() properly without playing or using the bridge
master as a net_device reference.
Florian Fainelli (4):
net: dsa: Call dsa_untag_bridge_pvid() from dsa_switch_rcv()
net: dsa: b53: Set untag_bridge_pvid
net: dsa: Obtain VLAN protocol from skb->protocol
net: dsa: Utilize __vlan_find_dev_deep_rcu()
drivers/net/dsa/b53/b53_common.c | 1 +
include/net/dsa.h | 8 ++++++++
net/dsa/dsa.c | 9 +++++++++
net/dsa/dsa_priv.h | 14 ++++----------
net/dsa/tag_brcm.c | 15 ++-------------
5 files changed, 24 insertions(+), 23 deletions(-)
--
2.25.1
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 <[email protected]>
---
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++;
--
2.25.1
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 <[email protected]>
---
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 = {
--
2.25.1
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 <[email protected]>
---
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);
--
2.25.1
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 <[email protected]>
---
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;
--
2.25.1
On Thu, Oct 01, 2020 at 07:42:12PM -0700, Florian Fainelli wrote:
> 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 <[email protected]>
> ---
Reviewed-by: Vladimir Oltean <[email protected]>
> 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++;
> --
> 2.25.1
>
On Thu, Oct 01, 2020 at 07:42:13PM -0700, Florian Fainelli wrote:
> 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 <[email protected]>
> ---
Reviewed-by: Vladimir Oltean <[email protected]>
> 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 = {
> --
> 2.25.1
>
On Thu, Oct 01, 2020 at 07:42:14PM -0700, Florian Fainelli wrote:
> 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 <[email protected]>
> ---
Reviewed-by: Vladimir Oltean <[email protected]>
> 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;
> --
> 2.25.1
>
On Thu, Oct 01, 2020 at 07:42:15PM -0700, Florian Fainelli wrote:
> 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 <[email protected]>
> ---
Reviewed-by: Vladimir Oltean <[email protected]>
> 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);
>
> --
> 2.25.1
>
From: Florian Fainelli <[email protected]>
Date: Thu, 1 Oct 2020 19:42:11 -0700
> Hi David, Jakub,
>
> This patch series is based on the recent discussions with Vladimir:
>
> https://lore.kernel.org/netdev/[email protected]/
>
> the simplest way forward was to call dsa_untag_bridge_pvid() after
> eth_type_trans() has been set which guarantees that skb->protocol is set
> to a correct value and this allows us to utilize
> __vlan_find_dev_deep_rcu() properly without playing or using the bridge
> master as a net_device reference.
Series applied, thanks.