summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerge Semin <fancer.lancer@gmail.com>2025-03-20 12:57:35 +0300
committerSerge Semin <fancer.lancer@gmail.com>2026-08-13 20:34:11 +0300
commite0f507d2d6e578e7209a9796641b1bfa81bc6b72 (patch)
tree828a501db5b30a07beddf55c3ec19d2d86204541
parentb677e3eaaede2a55acdefa38b100d4be6d4348b2 (diff)
downloadlinux-e0f507d2d6e578e7209a9796641b1bfa81bc6b72.tar.gz
linux-e0f507d2d6e578e7209a9796641b1bfa81bc6b72.zip
net: stmmac: vlan: Add VLAN S-TAG perfect filtering
The blamed commit added the Extended VLAN Tag Perfect filtering support to the DW Ether QoS part of the driver. Then the implementation has been reused for DW XGMACs in commit 534df0c1724b ("net: stmmac: dwxgmac2: Add support for HW-accelerated VLAN stripping"). Originally it was intended for the VLAN TAG perfect filters to be used orthogonally with the VLAN Tag Hash filtering. So one functionality would work irrespective to another one being available. Well, with some flaws but it has worked. The problem comes when you get to have a device with no VLAN Tag Hash filter but with the Extended VLAN Tag filter support. In such case by default the S-TAG filter won't be activated causing C-TAG frames filtering instead, because no ERSVLM or DOVLTC flag set in the MAC_VLAN_Tag_Filter register. These flags are responsible for VLAN S-TAG tag activating or disabling VLAN tag type checking. Anyway in case if the VLAN Tag Hash filtering is supported it will be _responsible_ for permitting both C- and S-Tags of the same VID, which at least isn't secure. But the VLAN perfect filter S-TAG entries will be misconfigured. Let's fix the problem denoted above by setting up the ERSVLM flag in case if the 802.1ad VLAN-protocol requested. The flag will be preserved in the private-data VIDs cache so to be properly restored after system resume and to differentiate the tags with the same VIDs but different types. Fixes: ed64639bc1e0 ("net: stmmac: Add support for VLAN Rx filtering") Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c68
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h2
2 files changed, 49 insertions, 21 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index 59469805edc2..1550843446a7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -7,14 +7,15 @@
#include "stmmac.h"
#include "stmmac_vlan.h"
-static void vlan_write_single(struct net_device *dev, u16 vid)
+static void vlan_write_single(struct net_device *dev, u32 data)
{
void __iomem *ioaddr = (void __iomem *)dev->base_addr;
- u32 val;
+ u32 val, mask;
- val = readl(ioaddr + VLAN_TAG);
- val &= ~VLAN_TAG_VID;
- val |= VLAN_TAG_ETV | vid;
+ mask = VLAN_TAG_ERSVLM | VLAN_TAG_VID;
+
+ val = readl(ioaddr + VLAN_TAG) & ~mask;
+ val |= VLAN_TAG_ETV | (data & mask);
writel(val, ioaddr + VLAN_TAG);
}
@@ -56,12 +57,15 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
__be16 proto, u16 vid)
{
int index = -1;
+ bool is_stag;
u32 val = 0;
int i, ret;
if (vid > 4095)
return -EINVAL;
+ is_stag = proto == htons(ETH_P_8021AD);
+
/* Single Rx VLAN Filter */
if (hw->num_vlan == 1) {
/* For single VLAN filter, VID 0 means VLAN promiscuous */
@@ -70,21 +74,30 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
return -EPERM;
}
+ val = vid;
+ if (is_stag)
+ val |= VLAN_TAG_ERSVLM;
+
+ if (hw->vlan_filter[0] == val)
+ return 0;
+
if (hw->vlan_filter[0] & VLAN_TAG_VID) {
netdev_err(dev, "Only single VLAN ID supported\n");
return -EPERM;
}
- hw->vlan_filter[0] = vid;
-
if (netif_running(dev))
- vlan_write_single(dev, vid);
+ vlan_write_single(dev, val);
+
+ hw->vlan_filter[0] = val;
return 0;
}
/* Extended Rx VLAN Filter Enable */
val |= VLAN_TAG_DATA_ETV | VLAN_TAG_DATA_VEN | vid;
+ if (is_stag)
+ val |= VLAN_TAG_DATA_ERSVLM;
for (i = 0; i < hw->num_vlan; i++) {
if (hw->vlan_filter[i] == val)
@@ -115,31 +128,44 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev,
__be16 proto, u16 vid)
{
int i, ret = 0;
+ bool is_stag;
+
+ is_stag = proto == htons(ETH_P_8021AD);
/* Single Rx VLAN Filter */
if (hw->num_vlan == 1) {
- if ((hw->vlan_filter[0] & VLAN_TAG_VID) == vid) {
- hw->vlan_filter[0] = 0;
+ if ((hw->vlan_filter[0] & VLAN_TAG_VID) != vid)
+ return 0;
+
+ if (is_stag != !!(hw->vlan_filter[0] & VLAN_TAG_ERSVLM))
+ return 0;
+
+ if (netif_running(dev))
+ vlan_write_single(dev, 0);
+
+ hw->vlan_filter[0] = 0;
- if (netif_running(dev))
- vlan_write_single(dev, 0);
- }
return 0;
}
/* Extended Rx VLAN Filter Enable */
for (i = 0; i < hw->num_vlan; i++) {
- if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VEN) &&
- ((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) == vid)) {
+ if (!(hw->vlan_filter[i] & VLAN_TAG_DATA_VEN))
+ continue;
- if (netif_running(dev)) {
- ret = vlan_write_filter(dev, hw, i, 0);
- if (ret)
- return ret;
- }
+ if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) != vid)
+ continue;
- hw->vlan_filter[i] = 0;
+ if (is_stag != !!(hw->vlan_filter[i] & VLAN_TAG_DATA_ERSVLM))
+ continue;
+
+ if (netif_running(dev)) {
+ ret = vlan_write_filter(dev, hw, i, 0);
+ if (ret)
+ return ret;
}
+
+ hw->vlan_filter[i] = 0;
}
return 0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h
index 514f28f246d9..5143c0fbda26 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.h
@@ -30,6 +30,7 @@
/* MAC VLAN Tag */
#define VLAN_TAG_VID GENMASK(15, 0)
#define VLAN_TAG_ETV BIT(16)
+#define VLAN_TAG_ERSVLM BIT(19)
/* MAC VLAN Tag Control */
#define VLAN_TAG_CTRL_OB BIT(0)
@@ -49,6 +50,7 @@
#define VLAN_TAG_DATA_VID GENMASK(15, 0)
#define VLAN_TAG_DATA_VEN BIT(16)
#define VLAN_TAG_DATA_ETV BIT(17)
+#define VLAN_TAG_DATA_ERSVLM BIT(19)
/* MAC VLAN HW FEAT */
#define HW_FEATURE3 0x00000128