net: stmmac: Defer VLAN HW configuration when interface is down

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

To fix this, check if the interface is up before accessing VLAN registers.
The software state is always kept up to date regardless of interface state.

When the interface is brought up, stmmac_vlan_restore() is called
to write the VLAN state to hardware.

Fixes: ed64639bc1 ("net: stmmac: Add support for VLAN Rx filtering")
Signed-off-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>
Link: https://patch.msgid.link/20260303145828.7845-5-ovidiu.panait.rb@renesas.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Ovidiu Panait 2026-03-03 14:58:28 +00:00 committed by Jakub Kicinski
parent bd7ad51253
commit 2cd70e3968
2 changed files with 26 additions and 19 deletions

View file

@ -6769,6 +6769,9 @@ static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
hash = 0;
}
if (!netif_running(priv->dev))
return 0;
return stmmac_update_vlan_hash(priv, priv->hw, hash, pmatch, is_double);
}

View file

@ -76,7 +76,9 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
}
hw->vlan_filter[0] = vid;
vlan_write_single(dev, vid);
if (netif_running(dev))
vlan_write_single(dev, vid);
return 0;
}
@ -97,12 +99,15 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
return -EPERM;
}
ret = vlan_write_filter(dev, hw, index, val);
if (netif_running(dev)) {
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,
@ -115,7 +120,9 @@ 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 (netif_running(dev))
vlan_write_single(dev, 0);
}
return 0;
}
@ -124,22 +131,23 @@ 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 (!ret)
hw->vlan_filter[i] = 0;
else
return ret;
if (netif_running(dev)) {
ret = vlan_write_filter(dev, hw, i, 0);
if (ret)
return ret;
}
hw->vlan_filter[i] = 0;
}
}
return ret;
return 0;
}
static void vlan_restore_hw_rx_fltr(struct net_device *dev,
struct mac_device_info *hw)
{
u32 val;
int i;
/* Single Rx VLAN Filter */
@ -149,12 +157,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]);
}
static void vlan_update_hash(struct mac_device_info *hw, u32 hash,