summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerge Semin <fancer.lancer@gmail.com>2025-04-18 18:56:39 +0300
committerSerge Semin <fancer.lancer@gmail.com>2026-08-13 20:35:10 +0300
commit2c6ad51635c5ca38a674bf33659db1ffb266dfa9 (patch)
treef0624eae0ab4e65408171897b1193f74f02752ac
parentdd88704ca02f19b5a755b3cfd1ea8d4ffb16f234 (diff)
downloadlinux-2c6ad51635c5ca38a674bf33659db1ffb266dfa9.tar.gz
linux-2c6ad51635c5ca38a674bf33659db1ffb266dfa9.zip
net: stmmac: vlan: Convert VLAN filters feature to being switchable
Currently if the kernel is built with the VLAN_8021Q config enabled the HW-offloaded VLAN packets filtering is fixed to be enabled and can't be disabled unless the Promiscuous mode is on. This can be fixed so the system administrators could switch the feature on and off when required by means of the ethtool -K command. The denoted functionality can be implemented by moving the MAC_Packet_Filter.VTFE flag switching to the stmmac_ops::set_hw_vlan_mode() callbacks and making sure it's toggled depending on the NETIF_F_HW_VLAN_(C|S)TAG_FILTER feature flag state. But the stmmac_ops::set_filter() callback will still need to be able to permit the VLAN traffic on the Promiscuous mode. Sadly the MAC_Packet_Filter.PR flag doesn't do that, but the MAC_Packet_Filter.RA flag do. Basically both of these flags are responsible for the same functionality - to make the Address Filtering module passing all incoming packets irrespective of the destination or source address. But the MAC_Packet_Filter.RA flag also let the packets to bypass all the filters including the VLAN one preserving the filters status. That fact is utilized by the MAC to deliver the filtering status to the DMA-descriptor and to decide to which Fail-queue the packets must be routed. Here is a table [1] describing the semantics of the denoted flags: +----+-------+--------------+-------------+-------+------------+ | | | SA/DA Filter | VLAN Filter | | Queue | | RA | VTFE | result | result | VFFQE | Routing | +----+-------+--------------+-------------+-------+------------+ | 0 | 0 | PASS | FAIL | 1 | VFFQ | | 0 | 1 | PASS | FAIL | X | DROP | | 1 | X | FAIL | X | 1 | UFFQ*/VFFQ | | 1 | X | PASS | FAIL | 1 | VFFQ | +----+-------+--------------+-------------+-------+------------+ * where X - don't care condition. So by setting the RA flag not only the Promiscouse mode is permitted but the VLAN Fail-queue feature is left supported (currently for the DW QoS Ether devices). So to speak the implemented change not only converts the HW-offloaded VLAN tags filter being switchable, but also preserve the VLAN Fail-queue feature supported in the driver, about which the commit a7602e7332b9 ("net: stmmac: don't reject VLANs when IFF_PROMISC is set") author worried. Note surprisingly the MAC_Packet_Filter CSR layout completely match on all the currently supported IP-cores: DW QoS Ether, DW XGMAC and DW XLGMAC. [1] DesignWare Cores Ethernet Quality-of-Service Databook, Revision 5.20a, April 2020, pp. 241-242. Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c15
5 files changed, 33 insertions, 19 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index c7a3097472e7..5d95acfc748f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -534,11 +534,10 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
value |= GMAC_RXQCTRL_VFFQE |
(hw->vlan_fail_q << GMAC_RXQCTRL_VFFQ_SHIFT);
writel(value, ioaddr + GMAC_RXQ_CTRL4);
- value = GMAC_PACKET_FILTER_PR | GMAC_PACKET_FILTER_RA;
- } else {
- value = GMAC_PACKET_FILTER_PR | GMAC_PACKET_FILTER_PCF;
}
-
+ value |= GMAC_PACKET_FILTER_RA;
+ value |= GMAC_PACKET_FILTER_PR;
+ value |= GMAC_PACKET_FILTER_PCF;
} else if ((dev->flags & IFF_ALLMULTI) ||
(netdev_mc_count(dev) > hw->multicast_filter_bins)) {
/* Pass all multi */
@@ -594,12 +593,8 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
}
/* VLAN filtering */
- if (dev->flags & IFF_PROMISC && !hw->vlan_fail_q_en) {
- value &= ~GMAC_PACKET_FILTER_VTFE;
- } else if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
- value |= GMAC_PACKET_FILTER_VTFE;
+ if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
stmmac_restore_hw_vlan_rx_fltr(priv, dev, hw);
- }
writel(value, ioaddr + GMAC_PACKET_FILTER);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index 9cd58b0d8306..197e9d5dd6db 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -577,6 +577,7 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
u32 value, mc_filter[8];
value = readl(ioaddr + XGMAC_PACKET_FILTER);
+ value &= ~XGMAC_FILTER_RA;
value &= ~XGMAC_FILTER_PCF;
value &= ~XGMAC_FILTER_PM;
value &= ~XGMAC_FILTER_HMC;
@@ -587,6 +588,7 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
memset(mc_filter, 0, sizeof(mc_filter));
if (dev->flags & IFF_PROMISC) {
+ value |= XGMAC_FILTER_RA;
value |= XGMAC_FILTER_PR;
value |= XGMAC_FILTER_PCF;
} else if ((dev->flags & IFF_ALLMULTI) ||
@@ -626,12 +628,8 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
}
/* VLAN filtering */
- if (dev->flags & IFF_PROMISC) {
- value &= ~XGMAC_FILTER_VTFE;
- } else if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
- value |= XGMAC_FILTER_VTFE;
+ if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
stmmac_restore_hw_vlan_rx_fltr(priv, dev, hw);
- }
writel(value, ioaddr + XGMAC_PACKET_FILTER);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index b8fd4b527393..b332d2a3bf27 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -633,7 +633,7 @@ struct stmmac_vlan_ops {
void (*rx_hw_vlan)(struct mac_device_info *hw, struct dma_desc *rx_desc,
struct sk_buff *skb);
void (*set_hw_vlan_mode)(struct mac_device_info *hw, bool rx_strip,
- bool rx_stag, bool tx_stag);
+ bool rx_ctag, bool rx_stag, bool tx_stag);
int (*add_hw_vlan_rx_fltr)(struct net_device *dev,
struct mac_device_info *hw,
__be16 proto, u16 vid);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index d2006d90e7e9..58a4ce67fe19 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -3895,6 +3895,7 @@ static int stmmac_hw_setup(struct net_device *dev)
stmmac_set_hw_vlan_mode(priv, priv->hw,
dev->features & (NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_STAG_RX),
+ dev->features & NETIF_F_HW_VLAN_CTAG_FILTER,
dev->features & NETIF_F_HW_VLAN_STAG_FILTER,
dev->features & NETIF_F_HW_VLAN_STAG_TX);
phylink_rx_clk_stop_unblock(priv->phylink);
@@ -6474,6 +6475,12 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
if (!(features & NETIF_F_RXCSUM))
features &= ~NETIF_F_RXHASH;
+ /* VLAN-filters work for both VLAN tag types */
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ features |= NETIF_F_HW_VLAN_STAG_FILTER;
+ else if (features & NETIF_F_HW_VLAN_STAG_FILTER)
+ features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
/* VLAN-stripping implies removing any VLAN tag type: CTAG and STAG */
if (dwmac_is_xmac(priv->plat->core_type)) {
if (features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -6530,6 +6537,7 @@ static int stmmac_set_features(struct net_device *netdev,
stmmac_set_hw_vlan_mode(priv, priv->hw,
features & (NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_STAG_RX),
+ features & NETIF_F_HW_VLAN_CTAG_FILTER,
features & NETIF_F_HW_VLAN_STAG_FILTER,
features & NETIF_F_HW_VLAN_STAG_TX);
phylink_rx_clk_stop_unblock(priv->phylink);
@@ -8790,8 +8798,10 @@ static int __stmmac_dvr_probe(struct device *device,
ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX;
if (priv->dma_cap.vlhash) {
- ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
- ndev->features |= NETIF_F_HW_VLAN_STAG_FILTER;
+ ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
+ ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
}
/* VLAN Insertion feature supports only one type at a time */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index b6d4b2c7a2d3..2cd79672c739 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -4,6 +4,7 @@
* stmmac VLAN (802.1Q) handling
*/
+#include "dwmac4.h"
#include "stmmac.h"
#include "stmmac_vlan.h"
@@ -245,11 +246,21 @@ static void vlan_rx_hw(struct mac_device_info *hw,
}
static void vlan_set_hw_mode(struct mac_device_info *hw, bool rx_strip,
- bool rx_stag, bool tx_stag)
+ bool rx_ctag, bool rx_stag, bool tx_stag)
{
void __iomem *ioaddr = hw->pcsr;
- u32 value = readl(ioaddr + VLAN_TAG);
+ u32 value;
+
+ /* Activate VLAN Tag Rx filters */
+ value = readl(ioaddr + GMAC_PACKET_FILTER);
+ if (rx_ctag || rx_stag)
+ value |= GMAC_PACKET_FILTER_VTFE;
+ else
+ value &= ~GMAC_PACKET_FILTER_VTFE;
+ writel(value, ioaddr + GMAC_PACKET_FILTER);
+ /* Setup Rx VLAN Tag stripping */
+ value = readl(ioaddr + VLAN_TAG);
value &= ~VLAN_TAG_CTRL_EVLS_MASK;
if (rx_strip)