Commit 2cd70e39 authored by Ovidiu Panait's avatar Ovidiu Panait Committed by Jakub Kicinski
Browse files

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: ed64639b ("net: stmmac: Add support for VLAN Rx filtering")
Signed-off-by: default avatarOvidiu Panait <ovidiu.panait.rb@renesas.com>
Link: https://patch.msgid.link/20260303145828.7845-5-ovidiu.panait.rb@renesas.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent bd7ad512
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);
}

+23 −19
Original line number Diff line number Diff line
@@ -76,6 +76,8 @@ static int vlan_add_hw_rx_fltr(struct net_device *dev,
		}

		hw->vlan_filter[0] = 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;
	}

	if (netif_running(dev)) {
		ret = vlan_write_filter(dev, hw, index, val);
		if (ret)
			return ret;
	}

	if (!ret)
	hw->vlan_filter[index] = val;

	return ret;
	return 0;
}

static int vlan_del_hw_rx_fltr(struct net_device *dev,
@@ -115,6 +120,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;

			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)) {

			if (netif_running(dev)) {
				ret = vlan_write_filter(dev, hw, i, 0);
				if (ret)
					return ret;
			}

			if (!ret)
			hw->vlan_filter[i] = 0;
			else
				return ret;
		}
	}

	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,