[PATCH net] net: usb: lan78xx: restore VLAN filter table after device reset

From: Nicolai Buchwitz

Date: Thu Jun 18 2026 - 15:12:13 EST


Configured VLANs stop receiving traffic after a USB autosuspend/resume
cycle, e.g. when a cable is unplugged long enough for the device to
suspend and then plugged back in. VLAN filtering stays enabled but all
VLAN-tagged frames are dropped until a VLAN is added or removed again.

The reset on resume clears the hardware VLAN filter table, but unlike
the multicast and address filters it is never reprogrammed from the
driver's shadow copy, so it stays empty.

Restore the VLAN filter table as part of the reset sequence.

Reported-by: Sven Schuchmann <schuchmann@xxxxxxxxxxxxxxxxx>
Closes: https://lore.kernel.org/netdev/BEZP281MB224501E38B30BFDC4BD3D364D9E32@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/T/#u
Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver")
Signed-off-by: Nicolai Buchwitz <nb@xxxxxxxxxxx>
---
drivers/net/usb/lan78xx.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index bcf293ea1bd3..52c76de64eb9 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -3065,14 +3065,20 @@ static int lan78xx_set_features(struct net_device *netdev,
return lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
}

+static int lan78xx_write_vlan_table(struct lan78xx_net *dev)
+{
+ struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
+
+ return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0,
+ DP_SEL_VHF_VLAN_LEN, pdata->vlan_table);
+}
+
static void lan78xx_deferred_vlan_write(struct work_struct *param)
{
struct lan78xx_priv *pdata =
container_of(param, struct lan78xx_priv, set_vlan);
- struct lan78xx_net *dev = pdata->dev;

- lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0,
- DP_SEL_VHF_VLAN_LEN, pdata->vlan_table);
+ lan78xx_write_vlan_table(pdata->dev);
}

static int lan78xx_vlan_rx_add_vid(struct net_device *netdev,
@@ -3353,6 +3359,15 @@ static int lan78xx_reset(struct lan78xx_net *dev)

lan78xx_set_multicast(dev->net);

+ /* The chip reset above also clears the VLAN filter table held in the
+ * shared VLAN/DA hash RAM. The network stack does not re-add VLANs
+ * after a silent device reset (e.g. on reset_resume after USB
+ * autosuspend), so restore the table from our shadow copy here.
+ */
+ ret = lan78xx_write_vlan_table(dev);
+ if (ret < 0)
+ return ret;
+
/* reset PHY */
ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
if (ret < 0)

base-commit: 7d8297e26b4e20b5d1c3c3fe51fe81a1c7fbc823
--
2.53.0