]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: stmmac: Defer VLAN HW configuration when interface is down
authorOvidiu Panait <ovidiu.panait.rb@renesas.com>
Tue, 3 Mar 2026 14:58:28 +0000 (14:58 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 5 Mar 2026 02:48:49 +0000 (18:48 -0800)
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: ed64639bc1e0 ("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>
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c

index 6d4ce35e990f2e0dbcdc3edb5489aa354ae84431..6827c99bde8c22db42b363d2d36ad6f26075ed50 100644 (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);
 }
 
index fcc34867405ed823521048da13351859b6142efd..e24efe3bfedbec48eb4dae1e5496e304fa28aa90 100644 (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,