* [PATCH net-next 1/4] net: stmmac: Fix error handling in VLAN add and delete paths
2026-02-23 12:40 [PATCH net-next 0/4] net: stmmac: Fix VLAN handling when interface is down Ovidiu Panait
@ 2026-02-23 12:40 ` Ovidiu Panait
2026-02-23 12:41 ` [PATCH net-next 2/4] net: stmmac: Improve double VLAN handling Ovidiu Panait
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Ovidiu Panait @ 2026-02-23 12:40 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, linux, rmk+kernel, maxime.chevallier,
boon.khai.ng, rohan.g.thomas, vladimir.oltean, hayashi.kunihiko,
matthew.gerlach, vee.khee.wong, boon.leong.ong, kim.tatt.chuah
Cc: netdev, linux-arm-kernel, linux-stm32, linux-kernel
stmmac_vlan_rx_add_vid() updates active_vlans and the VLAN hash
register before writing the HW filter entry. If the filter write
fails, it leaves a stale VID in active_vlans and the hash register.
stmmac_vlan_rx_kill_vid() has the reverse problem: it clears
active_vlans before removing the HW filter. On failure, the VID is
gone from active_vlans but still present in the HW filter table.
To fix this, reorder the operations to update the hash table first,
then attempt the HW filter operation. If the HW filter fails, roll
back both the active_vlans bitmap and the hash table by calling
stmmac_vlan_update() again.
Fixes: ed64639bc1e0 ("net: stmmac: Add support for VLAN Rx filtering")
Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 82375d34ad57..f2f120ddba46 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -6798,9 +6798,13 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
if (priv->hw->num_vlan) {
ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
- if (ret)
+ if (ret) {
+ clear_bit(vid, priv->active_vlans);
+ stmmac_vlan_update(priv, is_double);
goto err_pm_put;
+ }
}
+
err_pm_put:
pm_runtime_put(priv->device);
@@ -6824,15 +6828,21 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
is_double = true;
clear_bit(vid, priv->active_vlans);
+ ret = stmmac_vlan_update(priv, is_double);
+ if (ret) {
+ set_bit(vid, priv->active_vlans);
+ goto del_vlan_error;
+ }
if (priv->hw->num_vlan) {
ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
- if (ret)
+ if (ret) {
+ set_bit(vid, priv->active_vlans);
+ stmmac_vlan_update(priv, is_double);
goto del_vlan_error;
+ }
}
- ret = stmmac_vlan_update(priv, is_double);
-
del_vlan_error:
pm_runtime_put(priv->device);
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next 2/4] net: stmmac: Improve double VLAN handling
2026-02-23 12:40 [PATCH net-next 0/4] net: stmmac: Fix VLAN handling when interface is down Ovidiu Panait
2026-02-23 12:40 ` [PATCH net-next 1/4] net: stmmac: Fix error handling in VLAN add and delete paths Ovidiu Panait
@ 2026-02-23 12:41 ` Ovidiu Panait
2026-02-23 12:41 ` [PATCH net-next 3/4] net: stmmac: Add write_hw parameter to VLAN filter operations Ovidiu Panait
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Ovidiu Panait @ 2026-02-23 12:41 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, linux, rmk+kernel, maxime.chevallier,
boon.khai.ng, rohan.g.thomas, vladimir.oltean, hayashi.kunihiko,
matthew.gerlach, vee.khee.wong, boon.leong.ong, kim.tatt.chuah
Cc: netdev, linux-arm-kernel, linux-stm32, linux-kernel
The double VLAN bits (EDVLP, ESVL, DOVLTC) are handled inconsistently
between the two vlan_update_hash() implementations:
- dwxgmac2_update_vlan_hash() explicitly clears the double VLAN bits when
is_double is false, meaning that adding a 802.1Q VLAN will disable
double VLAN mode:
$ ip link add link eth0 name eth0.200 type vlan id 200 protocol 802.1ad
$ ip link add link eth0 name eth0.100 type vlan id 100
# Double VLAN bits no longer set
- vlan_update_hash() sets these bits and only clears them when the last
VLAN has been removed, so double VLAN mode remains enabled even after all
802.1AD VLANs are removed.
Address both issues by tracking the number of active 802.1AD VLANs in
priv->num_double_vlans. Pass this count to stmmac_vlan_update() so both
implementations correctly set the double VLAN bits when any 802.1AD
VLAN is active, and clear them only when none remain.
Also update vlan_update_hash() to explicitly clear the double VLAN bits
when is_double is false, matching the dwxgmac2 behavior.
Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 +
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 16 ++++++++++++----
.../net/ethernet/stmicro/stmmac/stmmac_vlan.c | 8 ++++++++
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 51c96a738151..33667a26708c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -323,6 +323,7 @@ struct stmmac_priv {
void __iomem *ptpaddr;
void __iomem *estaddr;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ unsigned int num_double_vlans;
int sfty_irq;
int sfty_ce_irq;
int sfty_ue_irq;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f2f120ddba46..45f2f3492dbd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -6779,6 +6779,7 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
+ unsigned int num_double_vlans;
bool is_double = false;
int ret;
@@ -6790,7 +6791,8 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
is_double = true;
set_bit(vid, priv->active_vlans);
- ret = stmmac_vlan_update(priv, is_double);
+ num_double_vlans = priv->num_double_vlans + is_double;
+ ret = stmmac_vlan_update(priv, num_double_vlans);
if (ret) {
clear_bit(vid, priv->active_vlans);
goto err_pm_put;
@@ -6800,11 +6802,13 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
if (ret) {
clear_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, is_double);
+ stmmac_vlan_update(priv, priv->num_double_vlans);
goto err_pm_put;
}
}
+ priv->num_double_vlans = num_double_vlans;
+
err_pm_put:
pm_runtime_put(priv->device);
@@ -6817,6 +6821,7 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
+ unsigned int num_double_vlans;
bool is_double = false;
int ret;
@@ -6828,7 +6833,8 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
is_double = true;
clear_bit(vid, priv->active_vlans);
- ret = stmmac_vlan_update(priv, is_double);
+ num_double_vlans = priv->num_double_vlans - is_double;
+ ret = stmmac_vlan_update(priv, num_double_vlans);
if (ret) {
set_bit(vid, priv->active_vlans);
goto del_vlan_error;
@@ -6838,11 +6844,13 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
if (ret) {
set_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, is_double);
+ stmmac_vlan_update(priv, priv->num_double_vlans);
goto del_vlan_error;
}
}
+ priv->num_double_vlans = num_double_vlans;
+
del_vlan_error:
pm_runtime_put(priv->device);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index b18404dd5a8b..de1a70e1c86e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -183,6 +183,10 @@ static void vlan_update_hash(struct mac_device_info *hw, u32 hash,
value |= VLAN_EDVLP;
value |= VLAN_ESVL;
value |= VLAN_DOVLTC;
+ } else {
+ value &= ~VLAN_EDVLP;
+ value &= ~VLAN_ESVL;
+ value &= ~VLAN_DOVLTC;
}
writel(value, ioaddr + VLAN_TAG);
@@ -193,6 +197,10 @@ static void vlan_update_hash(struct mac_device_info *hw, u32 hash,
value |= VLAN_EDVLP;
value |= VLAN_ESVL;
value |= VLAN_DOVLTC;
+ } else {
+ value &= ~VLAN_EDVLP;
+ value &= ~VLAN_ESVL;
+ value &= ~VLAN_DOVLTC;
}
writel(value | perfect_match, ioaddr + VLAN_TAG);
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next 3/4] net: stmmac: Add write_hw parameter to VLAN filter operations
2026-02-23 12:40 [PATCH net-next 0/4] net: stmmac: Fix VLAN handling when interface is down Ovidiu Panait
2026-02-23 12:40 ` [PATCH net-next 1/4] net: stmmac: Fix error handling in VLAN add and delete paths Ovidiu Panait
2026-02-23 12:41 ` [PATCH net-next 2/4] net: stmmac: Improve double VLAN handling Ovidiu Panait
@ 2026-02-23 12:41 ` Ovidiu Panait
2026-02-23 12:41 ` [PATCH net-next 4/4] net: stmmac: Defer VLAN HW configuration when interface is down Ovidiu Panait
2026-02-23 13:46 ` [PATCH net-next 0/4] net: stmmac: Fix VLAN handling " Russell King (Oracle)
4 siblings, 0 replies; 7+ messages in thread
From: Ovidiu Panait @ 2026-02-23 12:41 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, linux, rmk+kernel, maxime.chevallier,
boon.khai.ng, rohan.g.thomas, vladimir.oltean, hayashi.kunihiko,
matthew.gerlach, vee.khee.wong, boon.leong.ong, kim.tatt.chuah
Cc: netdev, linux-arm-kernel, linux-stm32, linux-kernel
Add a write_hw parameter to the VLAN add/delete HW filter functions and
to stmmac_vlan_update(). This flag controls whether the actual hardware
register accesses are performed. When set to false, only the software
state is updated.
The next commit will use this to defer hardware writes when the
interface is down.
No functional change.
Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>
---
drivers/net/ethernet/stmicro/stmmac/hwif.h | 6 ++--
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 23 ++++++++-----
.../net/ethernet/stmicro/stmmac/stmmac_vlan.c | 34 ++++++++++++-------
3 files changed, 40 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 0db96a387259..d7598c76251f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -647,10 +647,12 @@ struct stmmac_vlan_ops {
void (*set_hw_vlan_mode)(struct mac_device_info *hw);
int (*add_hw_vlan_rx_fltr)(struct net_device *dev,
struct mac_device_info *hw,
- __be16 proto, u16 vid);
+ __be16 proto, u16 vid,
+ bool write_hw);
int (*del_hw_vlan_rx_fltr)(struct net_device *dev,
struct mac_device_info *hw,
- __be16 proto, u16 vid);
+ __be16 proto, u16 vid,
+ bool write_hw);
void (*restore_hw_vlan_rx_fltr)(struct net_device *dev,
struct mac_device_info *hw);
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 45f2f3492dbd..536668a0d6dd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -6748,7 +6748,8 @@ static u32 stmmac_vid_crc32_le(__le16 vid_le)
return crc;
}
-static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
+static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double,
+ bool write_hw)
{
u32 crc, hash = 0;
u16 pmatch = 0;
@@ -6770,7 +6771,11 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
hash = 0;
}
- return stmmac_update_vlan_hash(priv, priv->hw, hash, pmatch, is_double);
+ if (write_hw)
+ return stmmac_update_vlan_hash(priv, priv->hw, hash, pmatch,
+ is_double);
+
+ return 0;
}
/* FIXME: This may need RXC to be running, but it may be called with BH
@@ -6792,17 +6797,18 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
set_bit(vid, priv->active_vlans);
num_double_vlans = priv->num_double_vlans + is_double;
- ret = stmmac_vlan_update(priv, num_double_vlans);
+ ret = stmmac_vlan_update(priv, num_double_vlans, true);
if (ret) {
clear_bit(vid, priv->active_vlans);
goto err_pm_put;
}
if (priv->hw->num_vlan) {
- ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
+ ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto,
+ vid, true);
if (ret) {
clear_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, priv->num_double_vlans);
+ stmmac_vlan_update(priv, priv->num_double_vlans, true);
goto err_pm_put;
}
}
@@ -6834,17 +6840,18 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
clear_bit(vid, priv->active_vlans);
num_double_vlans = priv->num_double_vlans - is_double;
- ret = stmmac_vlan_update(priv, num_double_vlans);
+ ret = stmmac_vlan_update(priv, num_double_vlans, true);
if (ret) {
set_bit(vid, priv->active_vlans);
goto del_vlan_error;
}
if (priv->hw->num_vlan) {
- ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
+ ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto,
+ vid, true);
if (ret) {
set_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, priv->num_double_vlans);
+ stmmac_vlan_update(priv, priv->num_double_vlans, true);
goto del_vlan_error;
}
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index de1a70e1c86e..b74c173da1b5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -53,7 +53,8 @@ static int vlan_write_filter(struct net_device *dev,
static int vlan_add_hw_rx_fltr(struct net_device *dev,
struct mac_device_info *hw,
- __be16 proto, u16 vid)
+ __be16 proto, u16 vid,
+ bool write_hw)
{
int index = -1;
u32 val = 0;
@@ -76,7 +77,8 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
}
hw->vlan_filter[0] = vid;
- vlan_write_single(dev, vid);
+ if (write_hw)
+ vlan_write_single(dev, vid);
return 0;
}
@@ -97,17 +99,21 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
return -EPERM;
}
- ret = vlan_write_filter(dev, hw, index, val);
+ if (write_hw) {
+ ret = vlan_write_filter(dev, hw, index, val);
+ if (ret)
+ return ret;
+ }
- if (!ret)
- hw->vlan_filter[index] = val;
+ hw->vlan_filter[index] = val;
- return ret;
+ return 0;
}
static int vlan_del_hw_rx_fltr(struct net_device *dev,
struct mac_device_info *hw,
- __be16 proto, u16 vid)
+ __be16 proto, u16 vid,
+ bool write_hw)
{
int i, ret = 0;
@@ -115,7 +121,8 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev,
if (hw->num_vlan == 1) {
if ((hw->vlan_filter[0] & VLAN_TAG_VID) == vid) {
hw->vlan_filter[0] = 0;
- vlan_write_single(dev, 0);
+ if (write_hw)
+ vlan_write_single(dev, 0);
}
return 0;
}
@@ -124,12 +131,13 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev,
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)) {
- ret = vlan_write_filter(dev, hw, i, 0);
+ if (write_hw) {
+ ret = vlan_write_filter(dev, hw, i, 0);
+ if (ret)
+ return ret;
+ }
- if (!ret)
- hw->vlan_filter[i] = 0;
- else
- return ret;
+ hw->vlan_filter[i] = 0;
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH net-next 4/4] net: stmmac: Defer VLAN HW configuration when interface is down
2026-02-23 12:40 [PATCH net-next 0/4] net: stmmac: Fix VLAN handling when interface is down Ovidiu Panait
` (2 preceding siblings ...)
2026-02-23 12:41 ` [PATCH net-next 3/4] net: stmmac: Add write_hw parameter to VLAN filter operations Ovidiu Panait
@ 2026-02-23 12:41 ` Ovidiu Panait
2026-02-23 13:46 ` [PATCH net-next 0/4] net: stmmac: Fix VLAN handling " Russell King (Oracle)
4 siblings, 0 replies; 7+ messages in thread
From: Ovidiu Panait @ 2026-02-23 12:41 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, linux, rmk+kernel, maxime.chevallier,
boon.khai.ng, rohan.g.thomas, vladimir.oltean, hayashi.kunihiko,
matthew.gerlach, vee.khee.wong, boon.leong.ong, kim.tatt.chuah
Cc: netdev, linux-arm-kernel, linux-stm32, linux-kernel
VLAN register accesses on the MAC side require the PHY RX clock to be
active. When the network interface is down, the PHY is suspended and
the RX clock is unavailable, causing VLAN operations to fail with
timeouts.
The VLAN core automatically removes VID 0 after the interface goes down
and re-adds it when it comes back up, so these timeouts happen during
normal interface down/up:
# ip link set end1 down
renesas-gbeth 15c40000.ethernet end1: Timeout accessing MAC_VLAN_Tag_Filter
renesas-gbeth 15c40000.ethernet end1: failed to kill vid 0081/0
Adding VLANs while the interface is down also fails:
# ip link add link end1 name end1.10 type vlan id 10
renesas-gbeth 15c40000.ethernet end1: Timeout accessing MAC_VLAN_Tag_Filter
RTNETLINK answers: Device or resource busy
Use the write_hw parameter introduced in the previous commit to skip
hardware register writes when the interface is down. The software state
is always kept up to date regardless of interface state.
When the interface is brought up, stmmac_vlan_configure() is called
to write the VLAN state to hardware.
Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 33 +++++++++++++++----
.../net/ethernet/stmicro/stmmac/stmmac_vlan.c | 9 ++---
2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 536668a0d6dd..d0aede23ae0d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -156,6 +156,7 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
u32 rxmode, u32 chan);
+static int stmmac_vlan_configure(struct stmmac_priv *priv);
#ifdef CONFIG_DEBUG_FS
static const struct net_device_ops stmmac_netdev_ops;
@@ -4111,6 +4112,14 @@ static int __stmmac_open(struct net_device *dev,
phylink_start(priv->phylink);
+ if (dev->features & NETIF_F_VLAN_FEATURES) {
+ phylink_rx_clk_stop_block(priv->phylink);
+ ret = stmmac_vlan_configure(priv);
+ phylink_rx_clk_stop_unblock(priv->phylink);
+ if (ret)
+ netdev_err(dev, "Failed to configure VLANs\n");
+ }
+
ret = stmmac_request_irq(dev);
if (ret)
goto irq_error;
@@ -6784,6 +6793,7 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double,
static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
+ bool write_hw = netif_running(ndev);
unsigned int num_double_vlans;
bool is_double = false;
int ret;
@@ -6797,7 +6807,7 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
set_bit(vid, priv->active_vlans);
num_double_vlans = priv->num_double_vlans + is_double;
- ret = stmmac_vlan_update(priv, num_double_vlans, true);
+ ret = stmmac_vlan_update(priv, num_double_vlans, write_hw);
if (ret) {
clear_bit(vid, priv->active_vlans);
goto err_pm_put;
@@ -6805,10 +6815,11 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
if (priv->hw->num_vlan) {
ret = stmmac_add_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto,
- vid, true);
+ vid, write_hw);
if (ret) {
clear_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, priv->num_double_vlans, true);
+ stmmac_vlan_update(priv, priv->num_double_vlans,
+ write_hw);
goto err_pm_put;
}
}
@@ -6827,6 +6838,7 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
+ bool write_hw = netif_running(ndev);
unsigned int num_double_vlans;
bool is_double = false;
int ret;
@@ -6840,7 +6852,7 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
clear_bit(vid, priv->active_vlans);
num_double_vlans = priv->num_double_vlans - is_double;
- ret = stmmac_vlan_update(priv, num_double_vlans, true);
+ ret = stmmac_vlan_update(priv, num_double_vlans, write_hw);
if (ret) {
set_bit(vid, priv->active_vlans);
goto del_vlan_error;
@@ -6848,10 +6860,11 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
if (priv->hw->num_vlan) {
ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto,
- vid, true);
+ vid, write_hw);
if (ret) {
set_bit(vid, priv->active_vlans);
- stmmac_vlan_update(priv, priv->num_double_vlans, true);
+ stmmac_vlan_update(priv, priv->num_double_vlans,
+ write_hw);
goto del_vlan_error;
}
}
@@ -6864,6 +6877,14 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
return ret;
}
+static int stmmac_vlan_configure(struct stmmac_priv *priv)
+{
+ if (priv->hw->num_vlan)
+ stmmac_restore_hw_vlan_rx_fltr(priv, priv->dev, priv->hw);
+
+ return stmmac_vlan_update(priv, priv->num_double_vlans, true);
+}
+
static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)
{
struct stmmac_priv *priv = netdev_priv(dev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index b74c173da1b5..070c11870c02 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -150,7 +150,6 @@ static void vlan_restore_hw_rx_fltr(struct net_device *dev,
void __iomem *ioaddr = hw->pcsr;
u32 value;
u32 hash;
- u32 val;
int i;
/* Single Rx VLAN Filter */
@@ -160,12 +159,8 @@ static void vlan_restore_hw_rx_fltr(struct net_device *dev,
}
/* Extended Rx VLAN Filter Enable */
- for (i = 0; i < hw->num_vlan; i++) {
- if (hw->vlan_filter[i] & VLAN_TAG_DATA_VEN) {
- val = hw->vlan_filter[i];
- vlan_write_filter(dev, hw, i, val);
- }
- }
+ for (i = 0; i < hw->num_vlan; i++)
+ vlan_write_filter(dev, hw, i, hw->vlan_filter[i]);
hash = readl(ioaddr + VLAN_HASH_TABLE);
if (hash & VLAN_VLHT) {
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH net-next 0/4] net: stmmac: Fix VLAN handling when interface is down
2026-02-23 12:40 [PATCH net-next 0/4] net: stmmac: Fix VLAN handling when interface is down Ovidiu Panait
` (3 preceding siblings ...)
2026-02-23 12:41 ` [PATCH net-next 4/4] net: stmmac: Defer VLAN HW configuration when interface is down Ovidiu Panait
@ 2026-02-23 13:46 ` Russell King (Oracle)
2026-02-24 9:17 ` Ovidiu Panait
4 siblings, 1 reply; 7+ messages in thread
From: Russell King (Oracle) @ 2026-02-23 13:46 UTC (permalink / raw)
To: Ovidiu Panait
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, maxime.chevallier, boon.khai.ng, rohan.g.thomas,
vladimir.oltean, hayashi.kunihiko, matthew.gerlach, vee.khee.wong,
boon.leong.ong, kim.tatt.chuah, netdev, linux-arm-kernel,
linux-stm32, linux-kernel
On Mon, Feb 23, 2026 at 12:40:58PM +0000, Ovidiu Panait wrote:
> VLAN register accesses on the MAC side require the PHY RX clock to be
> active. When the network interface is down, the PHY is suspended and
> the RX clock is unavailable, causing VLAN operations to fail with
> timeouts.
>
> The VLAN core automatically removes VID 0 after the interface goes down
> and re-adds it when it comes back up, so these timeouts happen during
> normal interface down/up:
>
> # ip link set end1 down
> renesas-gbeth 15c40000.ethernet end1: Timeout accessing MAC_VLAN_Tag_Filter
> renesas-gbeth 15c40000.ethernet end1: failed to kill vid 0081/0
>
> Adding VLANs while the interface is down also fails:
>
> # ip link add link end1 name end1.10 type vlan id 10
> renesas-gbeth 15c40000.ethernet end1: Timeout accessing MAC_VLAN_Tag_Filter
> RTNETLINK answers: Device or resource busy
>
> Patches 3-4 address this by deferring hardware writes when the
> interface is down and reconfiguring the VLAN state on interface up.
>
> Patches 1-2 fix some issues in the existing VLAN implementation.
First point to make is that when the netdev supports
NETIF_F_VLAN_FEATURES, receive clock stop is disabled. In stmmac:
/* Disable EEE RX clock stop to ensure VLAN register access works
* correctly.
*/
if (!(priv->plat->flags & STMMAC_FLAG_RX_CLK_RUNS_IN_LPI) &&
!(priv->dev->features & NETIF_F_VLAN_FEATURES))
config->eee_rx_clk_stop_enable = true;
in phylink:
if (pl->mac_supports_eee_ops) {
/* Explicitly configure whether the PHY is allowed to stop it's
* receive clock.
*/
ret = phy_eee_rx_clock_stop(phy,
pl->config->eee_rx_clk_stop_enable);
and also in phylink's phylink_rx_clk_stop_block():
/* Disable PHY receive clock stop if this is the first time this
* function has been called and clock-stop was previously enabled.
*/
if (pl->mac_rx_clk_stop_blocked++ == 0 &&
pl->mac_supports_eee_ops && pl->phydev &&
pl->config->eee_rx_clk_stop_enable)
phy_eee_rx_clock_stop(pl->phydev, false);
So, given that when stmmac supports VLAN, eee_rx_clk_stop_enable will be
false, so phylink_rx_clk_stop_block() does nothing useful and receive
clock stop at the PHY will be disabled.
So a few questions:
1) when the network interface is opened or resumed, a DMA reset is
performed which resets all hardware state, including VLAN state. On
resume, we call stmmac_restore_hw_vlan_rx_fltr(), but to me it looks
like that is incomplete, and bits of the VLAN configuration don't get
restored on resume. Please can you look at this and confirm whether
this is indeed the problem.
2) If we can fully restore the VLAN configuration on resume, I suspect
the driver will be doing the same work at resume as at open time, so
this code should be shared.
Please can you look at both of these points.
Thanks.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 7+ messages in thread* RE: [PATCH net-next 0/4] net: stmmac: Fix VLAN handling when interface is down
2026-02-23 13:46 ` [PATCH net-next 0/4] net: stmmac: Fix VLAN handling " Russell King (Oracle)
@ 2026-02-24 9:17 ` Ovidiu Panait
0 siblings, 0 replies; 7+ messages in thread
From: Ovidiu Panait @ 2026-02-24 9:17 UTC (permalink / raw)
To: Russell King
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, mcoquelin.stm32@gmail.com,
alexandre.torgue@foss.st.com, maxime.chevallier@bootlin.com,
boon.khai.ng@altera.com, rohan.g.thomas@altera.com,
vladimir.oltean@nxp.com, hayashi.kunihiko@socionext.com,
matthew.gerlach@altera.com, vee.khee.wong@intel.com,
boon.leong.ong@intel.com, kim.tatt.chuah@intel.com,
netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-stm32@st-md-mailman.stormreply.com,
linux-kernel@vger.kernel.org
Hi Russell,
> On Mon, Feb 23, 2026 at 12:40:58PM +0000, Ovidiu Panait wrote:
> > VLAN register accesses on the MAC side require the PHY RX clock to be
> > active. When the network interface is down, the PHY is suspended and
> > the RX clock is unavailable, causing VLAN operations to fail with
> > timeouts.
> >
> > The VLAN core automatically removes VID 0 after the interface goes down
> > and re-adds it when it comes back up, so these timeouts happen during
> > normal interface down/up:
> >
> > # ip link set end1 down
> > renesas-gbeth 15c40000.ethernet end1: Timeout accessing
> MAC_VLAN_Tag_Filter
> > renesas-gbeth 15c40000.ethernet end1: failed to kill vid 0081/0
> >
> > Adding VLANs while the interface is down also fails:
> >
> > # ip link add link end1 name end1.10 type vlan id 10
> > renesas-gbeth 15c40000.ethernet end1: Timeout accessing
> MAC_VLAN_Tag_Filter
> > RTNETLINK answers: Device or resource busy
> >
> > Patches 3-4 address this by deferring hardware writes when the
> > interface is down and reconfiguring the VLAN state on interface up.
> >
> > Patches 1-2 fix some issues in the existing VLAN implementation.
>
> First point to make is that when the netdev supports
> NETIF_F_VLAN_FEATURES, receive clock stop is disabled. In stmmac:
>
> /* Disable EEE RX clock stop to ensure VLAN register access works
> * correctly.
> */
> if (!(priv->plat->flags & STMMAC_FLAG_RX_CLK_RUNS_IN_LPI) &&
> !(priv->dev->features & NETIF_F_VLAN_FEATURES))
> config->eee_rx_clk_stop_enable = true;
>
> in phylink:
>
> if (pl->mac_supports_eee_ops) {
> /* Explicitly configure whether the PHY is allowed to stop
> it's
> * receive clock.
> */
> ret = phy_eee_rx_clock_stop(phy,
> pl->config-
> >eee_rx_clk_stop_enable);
>
> and also in phylink's phylink_rx_clk_stop_block():
>
> /* Disable PHY receive clock stop if this is the first time this
> * function has been called and clock-stop was previously enabled.
> */
> if (pl->mac_rx_clk_stop_blocked++ == 0 &&
> pl->mac_supports_eee_ops && pl->phydev &&
> pl->config->eee_rx_clk_stop_enable)
> phy_eee_rx_clock_stop(pl->phydev, false);
>
> So, given that when stmmac supports VLAN, eee_rx_clk_stop_enable will be
> false, so phylink_rx_clk_stop_block() does nothing useful and receive
> clock stop at the PHY will be disabled.
>
Thanks for pointing this out. I will drop the receive clock stop
block/unblock calls in the next version.
>
> So a few questions:
>
> 1) when the network interface is opened or resumed, a DMA reset is
> performed which resets all hardware state, including VLAN state. On
> resume, we call stmmac_restore_hw_vlan_rx_fltr(), but to me it looks
> like that is incomplete, and bits of the VLAN configuration don't get
> restored on resume. Please can you look at this and confirm whether
> this is indeed the problem.
>
I checked and calling only stmmac_restore_hw_vlan_rx_fltr() on resume
is not enough, the VLAN hash table and the VLAN_TAG control bits are
not being restored. stmmac_restore_hw_vlan_rx_fltr() also reads the
VLAN_HASH_TABLE register, which is always zero, due to the previous
DMA reset.
> 2) If we can fully restore the VLAN configuration on resume, I suspect
> the driver will be doing the same work at resume as at open time, so
> this code should be shared.
>
Yes, both resume and open need to restore the full VLAN state, so the same
code can be used for both paths.
I will prepare a new version for this series to include these changes.
Thanks!
Ovidiu
> Please can you look at both of these points.
>
> Thanks.
>
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 7+ messages in thread