Jiri Slaby ba7816
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Jiri Slaby ba7816
Date: Sat, 25 Mar 2023 13:28:15 +0200
Jiri Slaby ba7816
Subject: [PATCH] net: stmmac: don't reject VLANs when IFF_PROMISC is set
Jiri Slaby ba7816
References: bsc#1012628
Jiri Slaby ba7816
Patch-mainline: 6.2.10
Jiri Slaby ba7816
Git-commit: a7602e7332b97cfbec7bacb0f1ade99a575fe104
Jiri Slaby ba7816
Jiri Slaby ba7816
[ Upstream commit a7602e7332b97cfbec7bacb0f1ade99a575fe104 ]
Jiri Slaby ba7816
Jiri Slaby ba7816
The blamed commit has introduced the following tests to
Jiri Slaby ba7816
dwmac4_add_hw_vlan_rx_fltr(), called from stmmac_vlan_rx_add_vid():
Jiri Slaby ba7816
Jiri Slaby ba7816
	if (hw->promisc) {
Jiri Slaby ba7816
		netdev_err(dev,
Jiri Slaby ba7816
			   "Adding VLAN in promisc mode not supported\n");
Jiri Slaby ba7816
		return -EPERM;
Jiri Slaby ba7816
	}
Jiri Slaby ba7816
Jiri Slaby ba7816
"VLAN promiscuous" mode is keyed in this driver to IFF_PROMISC, and so,
Jiri Slaby ba7816
vlan_vid_add() and vlan_vid_del() calls cannot take place in IFF_PROMISC
Jiri Slaby ba7816
mode. I have the following 2 arguments that this restriction is.... hm,
Jiri Slaby ba7816
how shall I put it nicely... unproductive :)
Jiri Slaby ba7816
Jiri Slaby ba7816
First, take the case of a Linux bridge. If the kernel is compiled with
Jiri Slaby ba7816
CONFIG_BRIDGE_VLAN_FILTERING=y, then this bridge shall have a VLAN
Jiri Slaby ba7816
database. The bridge shall try to call vlan_add_vid() on its bridge
Jiri Slaby ba7816
ports for each VLAN in the VLAN table. It will do this irrespectively of
Jiri Slaby ba7816
whether that port is *currently* VLAN-aware or not. So it will do this
Jiri Slaby ba7816
even when the bridge was created with vlan_filtering 0.
Jiri Slaby ba7816
But the Linux bridge, in VLAN-unaware mode, configures its ports in
Jiri Slaby ba7816
promiscuous (IFF_PROMISC) mode, so that they accept packets with any
Jiri Slaby ba7816
MAC DA (a switch must do this in order to forward those packets which
Jiri Slaby ba7816
are not directly targeted to its MAC address).
Jiri Slaby ba7816
Jiri Slaby ba7816
As a result, the stmmac driver does not work as a bridge port, when the
Jiri Slaby ba7816
kernel is compiled with CONFIG_BRIDGE_VLAN_FILTERING=y.
Jiri Slaby ba7816
Jiri Slaby ba7816
$ ip link add br0 type bridge && ip link set br0 up
Jiri Slaby ba7816
$ ip link set eth0 master br0 && ip link set eth0 up
Jiri Slaby ba7816
[ 2333.943296] br0: port 1(eth0) entered blocking state
Jiri Slaby ba7816
[ 2333.943381] br0: port 1(eth0) entered disabled state
Jiri Slaby ba7816
[ 2333.943782] device eth0 entered promiscuous mode
Jiri Slaby ba7816
[ 2333.944080] 4033c000.ethernet eth0: Adding VLAN in promisc mode not supported
Jiri Slaby ba7816
[ 2333.976509] 4033c000.ethernet eth0: failed to initialize vlan filtering on this port
Jiri Slaby ba7816
RTNETLINK answers: Operation not permitted
Jiri Slaby ba7816
Jiri Slaby ba7816
Secondly, take the case of stmmac as DSA master. Some switch tagging
Jiri Slaby ba7816
protocols are based on 802.1Q VLANs (tag_sja1105.c), and as such,
Jiri Slaby ba7816
tag_8021q.c uses vlan_vid_add() to work with VLAN-filtering DSA masters.
Jiri Slaby ba7816
But also, when a DSA port becomes promiscuous (for example when it joins
Jiri Slaby ba7816
a bridge), the DSA framework also makes the DSA master promiscuous.
Jiri Slaby ba7816
Jiri Slaby ba7816
Moreover, for every VLAN that a DSA switch sends to the CPU, DSA also
Jiri Slaby ba7816
programs a VLAN filter on the DSA master, because if the the DSA switch
Jiri Slaby ba7816
uses a tail tag, then the hardware frame parser of the DSA master will
Jiri Slaby ba7816
see VLAN as VLAN, and might filter them out, for being unknown.
Jiri Slaby ba7816
Jiri Slaby ba7816
Due to the above 2 reasons, my belief is that the stmmac driver does not
Jiri Slaby ba7816
get to choose to not accept vlan_vid_add() calls while IFF_PROMISC is
Jiri Slaby ba7816
enabled, because the 2 are completely independent and there are code
Jiri Slaby ba7816
paths in the network stack which directly lead to this situation
Jiri Slaby ba7816
occurring, without the user's direct input.
Jiri Slaby ba7816
Jiri Slaby ba7816
In fact, my belief is that "VLAN promiscuous" mode should have never
Jiri Slaby ba7816
been keyed on IFF_PROMISC in the first place, but rather, on the
Jiri Slaby ba7816
NETIF_F_HW_VLAN_CTAG_FILTER feature flag which can be toggled by the
Jiri Slaby ba7816
user through ethtool -k, when present in netdev->hw_features.
Jiri Slaby ba7816
Jiri Slaby ba7816
In the stmmac driver, NETIF_F_HW_VLAN_CTAG_FILTER is only present in
Jiri Slaby ba7816
"features", making this feature "on [fixed]".
Jiri Slaby ba7816
Jiri Slaby ba7816
I have this belief because I am unaware of any definition of promiscuity
Jiri Slaby ba7816
which implies having an effect on anything other than MAC DA (therefore
Jiri Slaby ba7816
not VLAN). However, I seem to be rather alone in having this opinion,
Jiri Slaby ba7816
looking back at the disagreements from this discussion:
Jiri Slaby ba7816
https://lore.kernel.org/netdev/20201110153958.ci5ekor3o2ekg3ky@ipetronik.com/
Jiri Slaby ba7816
Jiri Slaby ba7816
In any case, to remove the vlan_vid_add() dependency on !IFF_PROMISC,
Jiri Slaby ba7816
one would need to remove the check and see what fails. I guess the test
Jiri Slaby ba7816
was there because of the way in which dwmac4_vlan_promisc_enable() is
Jiri Slaby ba7816
implemented.
Jiri Slaby ba7816
Jiri Slaby ba7816
For context, the dwmac4 supports Perfect Filtering for a limited number
Jiri Slaby ba7816
of VLANs - dwmac4_get_num_vlan(), priv->hw->num_vlan, with a fallback on
Jiri Slaby ba7816
Hash Filtering - priv->dma_cap.vlhash - see stmmac_vlan_update(), also
Jiri Slaby ba7816
visible in cat /sys/kernel/debug/stmmaceth/eth0/dma_cap | grep 'VLAN
Jiri Slaby ba7816
Hash Filtering'.
Jiri Slaby ba7816
Jiri Slaby ba7816
The perfect filtering is based on MAC_VLAN_Tag_Filter/MAC_VLAN_Tag_Data
Jiri Slaby ba7816
registers, accessed in the driver through dwmac4_write_vlan_filter().
Jiri Slaby ba7816
Jiri Slaby ba7816
The hash filtering is based on the MAC_VLAN_Hash_Table register, named
Jiri Slaby ba7816
GMAC_VLAN_HASH_TABLE in the driver and accessed by dwmac4_update_vlan_hash().
Jiri Slaby ba7816
The control bit for enabling hash filtering is GMAC_VLAN_VTHM
Jiri Slaby ba7816
(MAC_VLAN_Tag_Ctrl bit VTHM: VLAN Tag Hash Table Match Enable).
Jiri Slaby ba7816
Jiri Slaby ba7816
Now, the description of dwmac4_vlan_promisc_enable() is that it iterates
Jiri Slaby ba7816
through the driver's cache of perfect filter entries (hw->vlan_filter[i],
Jiri Slaby ba7816
added by dwmac4_add_hw_vlan_rx_fltr()), and evicts them from hardware by
Jiri Slaby ba7816
unsetting their GMAC_VLAN_TAG_DATA_VEN (MAC_VLAN_Tag_Data bit VEN - VLAN
Jiri Slaby ba7816
Tag Enable) bit. Then it unsets the GMAC_VLAN_VTHM bit, which disables
Jiri Slaby ba7816
hash matching.
Jiri Slaby ba7816
Jiri Slaby ba7816
This leaves the MAC, according to table "VLAN Match Status" from the
Jiri Slaby ba7816
documentation, to always enter these data paths:
Jiri Slaby ba7816
Jiri Slaby ba7816
VID    |VLAN Perfect Filter |VTHM Bit |VLAN Hash Filter |Final VLAN Match
Jiri Slaby ba7816
       |Match Result        |         |Match Result     |Status
Jiri Slaby ba7816
-------|--------------------|---------|-----------------|----------------
Jiri Slaby ba7816
VID!=0 |Fail                |0        |don't care       |Pass
Jiri Slaby ba7816
Jiri Slaby ba7816
So, dwmac4_vlan_promisc_enable() does its job, but by unsetting
Jiri Slaby ba7816
GMAC_VLAN_VTHM, it conflicts with the other code path which controls
Jiri Slaby ba7816
this bit: dwmac4_update_vlan_hash(), called through stmmac_update_vlan_hash()
Jiri Slaby ba7816
from stmmac_vlan_rx_add_vid() and from stmmac_vlan_rx_kill_vid().
Jiri Slaby ba7816
This is, I guess, why dwmac4_add_hw_vlan_rx_fltr() is not allowed to run
Jiri Slaby ba7816
after dwmac4_vlan_promisc_enable() has unset GMAC_VLAN_VTHM: because if
Jiri Slaby ba7816
it did, then dwmac4_update_vlan_hash() would set GMAC_VLAN_VTHM again,
Jiri Slaby ba7816
breaking the "VLAN promiscuity".
Jiri Slaby ba7816
Jiri Slaby ba7816
It turns out that dwmac4_vlan_promisc_enable() is way too complicated
Jiri Slaby ba7816
for what needs to be done. The MAC_Packet_Filter register also has the
Jiri Slaby ba7816
VTFE bit (VLAN Tag Filter Enable), which simply controls whether VLAN
Jiri Slaby ba7816
tagged packets which don't match the filtering tables (either perfect or
Jiri Slaby ba7816
hash) are dropped or not. At the moment, this driver unconditionally
Jiri Slaby ba7816
sets GMAC_PACKET_FILTER_VTFE if NETIF_F_HW_VLAN_CTAG_FILTER was detected
Jiri Slaby ba7816
through the priv->dma_cap.vlhash capability bits of the device, in
Jiri Slaby ba7816
stmmac_dvr_probe().
Jiri Slaby ba7816
Jiri Slaby ba7816
I would suggest deleting the unnecessarily complex logic from
Jiri Slaby ba7816
dwmac4_vlan_promisc_enable(), and simply unsetting GMAC_PACKET_FILTER_VTFE
Jiri Slaby ba7816
when becoming IFF_PROMISC, which has the same effect of allowing packets
Jiri Slaby ba7816
with any VLAN tags, but has the additional benefit of being able to run
Jiri Slaby ba7816
concurrently with stmmac_vlan_rx_add_vid() and stmmac_vlan_rx_kill_vid().
Jiri Slaby ba7816
Jiri Slaby ba7816
As much as I believe that the VTFE bit should have been exclusively
Jiri Slaby ba7816
controlled by NETIF_F_HW_VLAN_CTAG_FILTER through ethtool, and not by
Jiri Slaby ba7816
IFF_PROMISC, changing that is not a punctual fix to the problem, and it
Jiri Slaby ba7816
would probably break the VFFQ feature added by the later commit
Jiri Slaby ba7816
e0f9956a3862 ("net: stmmac: Add option for VLAN filter fail queue
Jiri Slaby ba7816
enable"). From the commit description, VFFQ needs IFF_PROMISC=on and
Jiri Slaby ba7816
VTFE=off in order to work (and this change respects that). But if VTFE
Jiri Slaby ba7816
was changed to be controlled through ethtool -k, then a user-visible
Jiri Slaby ba7816
change would have been introduced in Intel's scripts (a need to run
Jiri Slaby ba7816
"ethtool -k eth0 rx-vlan-filter off" which did not exist before).
Jiri Slaby ba7816
Jiri Slaby ba7816
The patch was tested with this set of commands:
Jiri Slaby ba7816
Jiri Slaby ba7816
  ip link set eth0 up
Jiri Slaby ba7816
  ip link add link eth0 name eth0.100 type vlan id 100
Jiri Slaby ba7816
  ip addr add 192.168.100.2/24 dev eth0.100 && ip link set eth0.100 up
Jiri Slaby ba7816
  ip link set eth0 promisc on
Jiri Slaby ba7816
  ip link add link eth0 name eth0.101 type vlan id 101
Jiri Slaby ba7816
  ip addr add 192.168.101.2/24 dev eth0.101 && ip link set eth0.101 up
Jiri Slaby ba7816
  ip link set eth0 promisc off
Jiri Slaby ba7816
  ping -c 5 192.168.100.1
Jiri Slaby ba7816
  ping -c 5 192.168.101.1
Jiri Slaby ba7816
  ip link set eth0 promisc on
Jiri Slaby ba7816
  ping -c 5 192.168.100.1
Jiri Slaby ba7816
  ping -c 5 192.168.101.1
Jiri Slaby ba7816
  ip link del eth0.100
Jiri Slaby ba7816
  ip link del eth0.101
Jiri Slaby ba7816
  # Wait for VLAN-tagged pings from the other end...
Jiri Slaby ba7816
  # Check with "tcpdump -i eth0 -e -n -p" and we should see them
Jiri Slaby ba7816
  ip link set eth0 promisc off
Jiri Slaby ba7816
  # Wait for VLAN-tagged pings from the other end...
Jiri Slaby ba7816
  # Check with "tcpdump -i eth0 -e -n -p" and we shouldn't see them
Jiri Slaby ba7816
  # anymore, but remove the "-p" argument from tcpdump and they're there.
Jiri Slaby ba7816
Jiri Slaby ba7816
Fixes: c89f44ff10fd ("net: stmmac: Add support for VLAN promiscuous mode")
Jiri Slaby ba7816
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Jiri Slaby ba7816
Signed-off-by: David S. Miller <davem@davemloft.net>
Jiri Slaby ba7816
Signed-off-by: Sasha Levin <sashal@kernel.org>
Jiri Slaby ba7816
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Jiri Slaby ba7816
---
Jiri Slaby ba7816
 drivers/net/ethernet/stmicro/stmmac/common.h  |  1 -
Jiri Slaby ba7816
 .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 61 +------------------
Jiri Slaby ba7816
 2 files changed, 3 insertions(+), 59 deletions(-)
Jiri Slaby ba7816
Jiri Slaby ba7816
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
Jiri Slaby ba7816
index ec9c1302..54bb072a 100644
Jiri Slaby ba7816
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
Jiri Slaby ba7816
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
Jiri Slaby ba7816
@@ -532,7 +532,6 @@ struct mac_device_info {
Jiri Slaby ba7816
 	unsigned int xlgmac;
Jiri Slaby ba7816
 	unsigned int num_vlan;
Jiri Slaby ba7816
 	u32 vlan_filter[32];
Jiri Slaby ba7816
-	unsigned int promisc;
Jiri Slaby ba7816
 	bool vlan_fail_q_en;
Jiri Slaby ba7816
 	u8 vlan_fail_q;
Jiri Slaby ba7816
 };
Jiri Slaby ba7816
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
Jiri Slaby ba7816
index 8c7a0b7c..36251ec2 100644
Jiri Slaby ba7816
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
Jiri Slaby ba7816
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
Jiri Slaby ba7816
@@ -472,12 +472,6 @@ static int dwmac4_add_hw_vlan_rx_fltr(struct net_device *dev,
Jiri Slaby ba7816
 	if (vid > 4095)
Jiri Slaby ba7816
 		return -EINVAL;
Jiri Slaby ba7816
 
Jiri Slaby ba7816
-	if (hw->promisc) {
Jiri Slaby ba7816
-		netdev_err(dev,
Jiri Slaby ba7816
-			   "Adding VLAN in promisc mode not supported\n");
Jiri Slaby ba7816
-		return -EPERM;
Jiri Slaby ba7816
-	}
Jiri Slaby ba7816
-
Jiri Slaby ba7816
 	/* Single Rx VLAN Filter */
Jiri Slaby ba7816
 	if (hw->num_vlan == 1) {
Jiri Slaby ba7816
 		/* For single VLAN filter, VID 0 means VLAN promiscuous */
Jiri Slaby ba7816
@@ -527,12 +521,6 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
Jiri Slaby ba7816
 {
Jiri Slaby ba7816
 	int i, ret = 0;
Jiri Slaby ba7816
 
Jiri Slaby ba7816
-	if (hw->promisc) {
Jiri Slaby ba7816
-		netdev_err(dev,
Jiri Slaby ba7816
-			   "Deleting VLAN in promisc mode not supported\n");
Jiri Slaby ba7816
-		return -EPERM;
Jiri Slaby ba7816
-	}
Jiri Slaby ba7816
-
Jiri Slaby ba7816
 	/* Single Rx VLAN Filter */
Jiri Slaby ba7816
 	if (hw->num_vlan == 1) {
Jiri Slaby ba7816
 		if ((hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) == vid) {
Jiri Slaby ba7816
@@ -557,39 +545,6 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
Jiri Slaby ba7816
 	return ret;
Jiri Slaby ba7816
 }
Jiri Slaby ba7816
 
Jiri Slaby ba7816
-static void dwmac4_vlan_promisc_enable(struct net_device *dev,
Jiri Slaby ba7816
-				       struct mac_device_info *hw)
Jiri Slaby ba7816
-{
Jiri Slaby ba7816
-	void __iomem *ioaddr = hw->pcsr;
Jiri Slaby ba7816
-	u32 value;
Jiri Slaby ba7816
-	u32 hash;
Jiri Slaby ba7816
-	u32 val;
Jiri Slaby ba7816
-	int i;
Jiri Slaby ba7816
-
Jiri Slaby ba7816
-	/* Single Rx VLAN Filter */
Jiri Slaby ba7816
-	if (hw->num_vlan == 1) {
Jiri Slaby ba7816
-		dwmac4_write_single_vlan(dev, 0);
Jiri Slaby ba7816
-		return;
Jiri Slaby ba7816
-	}
Jiri Slaby ba7816
-
Jiri Slaby ba7816
-	/* Extended Rx VLAN Filter Enable */
Jiri Slaby ba7816
-	for (i = 0; i < hw->num_vlan; i++) {
Jiri Slaby ba7816
-		if (hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VEN) {
Jiri Slaby ba7816
-			val = hw->vlan_filter[i] & ~GMAC_VLAN_TAG_DATA_VEN;
Jiri Slaby ba7816
-			dwmac4_write_vlan_filter(dev, hw, i, val);
Jiri Slaby ba7816
-		}
Jiri Slaby ba7816
-	}
Jiri Slaby ba7816
-
Jiri Slaby ba7816
-	hash = readl(ioaddr + GMAC_VLAN_HASH_TABLE);
Jiri Slaby ba7816
-	if (hash & GMAC_VLAN_VLHT) {
Jiri Slaby ba7816
-		value = readl(ioaddr + GMAC_VLAN_TAG);
Jiri Slaby ba7816
-		if (value & GMAC_VLAN_VTHM) {
Jiri Slaby ba7816
-			value &= ~GMAC_VLAN_VTHM;
Jiri Slaby ba7816
-			writel(value, ioaddr + GMAC_VLAN_TAG);
Jiri Slaby ba7816
-		}
Jiri Slaby ba7816
-	}
Jiri Slaby ba7816
-}
Jiri Slaby ba7816
-
Jiri Slaby ba7816
 static void dwmac4_restore_hw_vlan_rx_fltr(struct net_device *dev,
Jiri Slaby ba7816
 					   struct mac_device_info *hw)
Jiri Slaby ba7816
 {
Jiri Slaby ba7816
@@ -709,22 +664,12 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
Jiri Slaby ba7816
 	}
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 	/* VLAN filtering */
Jiri Slaby ba7816
-	if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
Jiri Slaby ba7816
+	if (dev->flags & IFF_PROMISC && !hw->vlan_fail_q_en)
Jiri Slaby ba7816
+		value &= ~GMAC_PACKET_FILTER_VTFE;
Jiri Slaby ba7816
+	else if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
Jiri Slaby ba7816
 		value |= GMAC_PACKET_FILTER_VTFE;
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 	writel(value, ioaddr + GMAC_PACKET_FILTER);
Jiri Slaby ba7816
-
Jiri Slaby ba7816
-	if (dev->flags & IFF_PROMISC && !hw->vlan_fail_q_en) {
Jiri Slaby ba7816
-		if (!hw->promisc) {
Jiri Slaby ba7816
-			hw->promisc = 1;
Jiri Slaby ba7816
-			dwmac4_vlan_promisc_enable(dev, hw);
Jiri Slaby ba7816
-		}
Jiri Slaby ba7816
-	} else {
Jiri Slaby ba7816
-		if (hw->promisc) {
Jiri Slaby ba7816
-			hw->promisc = 0;
Jiri Slaby ba7816
-			dwmac4_restore_hw_vlan_rx_fltr(dev, hw);
Jiri Slaby ba7816
-		}
Jiri Slaby ba7816
-	}
Jiri Slaby ba7816
 }
Jiri Slaby ba7816
 
Jiri Slaby ba7816
 static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,
Jiri Slaby ba7816
-- 
Jiri Slaby ba7816
2.35.3
Jiri Slaby ba7816