]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: stmmac: Improve double VLAN handling
authorOvidiu Panait <ovidiu.panait.rb@renesas.com>
Tue, 3 Mar 2026 14:58:26 +0000 (14:58 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 5 Mar 2026 02:48:49 +0000 (18:48 -0800)
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>
Link: https://patch.msgid.link/20260303145828.7845-3-ovidiu.panait.rb@renesas.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c

index 51c96a738151b7b0866b257796d1da583011f014..33667a26708c82439cdd13568ea6e964799ec2a2 100644 (file)
@@ -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;
index b6a127316ab5d70e2e9c9a855ced513119108054..01d181c2c68eae688a1c3e057d59d2ccc93271b7 100644 (file)
@@ -6775,6 +6775,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;
 
@@ -6786,7 +6787,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;
@@ -6796,11 +6798,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);
 
@@ -6813,6 +6817,7 @@ err_pm_put:
 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;
 
@@ -6824,7 +6829,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;
@@ -6834,11 +6840,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);
 
index b18404dd5a8bebd4f784c69f7837839e569217f7..de1a70e1c86ef5f9c84a42c91c3c4595967bc616 100644 (file)
@@ -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);