]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iavf: wait for PF confirmation before removing VLAN filters
authorPetr Oros <poros@redhat.com>
Tue, 28 Apr 2026 05:22:15 +0000 (22:22 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 30 Apr 2026 09:37:38 +0000 (11:37 +0200)
The VLAN filter DELETE path was asymmetric with the ADD path: ADD
waits for PF confirmation (ADD -> ADDING -> ACTIVE), but DELETE
immediately frees the filter struct after sending the DEL message
without waiting for the PF response.

This is problematic because:
 - If the PF rejects the DEL, the filter remains in HW but the driver
   has already freed the tracking structure, losing sync.
 - Race conditions between DEL pending and other operations
   (add, reset) cannot be properly resolved if the filter struct
   is already gone.

Add IAVF_VLAN_REMOVING state to make the DELETE path symmetric:

  REMOVE -> REMOVING (send DEL) -> PF confirms -> kfree
                                -> PF rejects  -> ACTIVE

In iavf_del_vlans(), transition filters from REMOVE to REMOVING
instead of immediately freeing them. The new DEL completion handler
in iavf_virtchnl_completion() frees filters on success or reverts
them to ACTIVE on error.

Update iavf_add_vlan() to handle the REMOVING state: if a DEL is
pending and the user re-adds the same VLAN, queue it for ADD so
it gets re-programmed after the PF processes the DEL.

The !VLAN_FILTERING_ALLOWED early-exit path still frees filters
directly since no PF message is sent in that case.

Also update iavf_del_vlan() to skip filters already in REMOVING
state: DEL has been sent to PF and the completion handler will
free the filter when PF confirms. Without this guard, the sequence
DEL(pending) -> user-del -> second DEL could cause the PF to return
an error for the second DEL (filter already gone), causing the
completion handler to incorrectly revert a deleted filter back to
ACTIVE.

Fixes: 968996c070ef ("iavf: Fix VLAN_V2 addition/rejection")
Signed-off-by: Petr Oros <poros@redhat.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Link: https://patch.msgid.link/20260427-jk-iwl-net-petr-oros-fixes-v1-3-cdcb48303fd8@intel.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/intel/iavf/iavf.h
drivers/net/ethernet/intel/iavf/iavf_main.c
drivers/net/ethernet/intel/iavf/iavf_virtchnl.c

index 5765715914d6b21a0630edab35607b8468cbcc76..050f8241ef5e6b7ed1a851d2127d85e0a03630b8 100644 (file)
@@ -161,6 +161,7 @@ enum iavf_vlan_state_t {
        IAVF_VLAN_ADDING,       /* ADD sent to PF, waiting for response */
        IAVF_VLAN_ACTIVE,       /* PF confirmed, filter is in HW */
        IAVF_VLAN_REMOVE,       /* filter queued for DEL from PF */
+       IAVF_VLAN_REMOVING,     /* DEL sent to PF, waiting for response */
 };
 
 struct iavf_vlan_filter {
index ca29038c00162ffc95a0643f73ddfa4669046fe6..d2914c511e1e0374e9721ea769ec27db1cacff2f 100644 (file)
@@ -757,10 +757,10 @@ iavf_vlan_filter *iavf_add_vlan(struct iavf_adapter *adapter,
                adapter->num_vlan_filters++;
                iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_VLAN_FILTER);
        } else if (f->state == IAVF_VLAN_REMOVE) {
-               /* Re-add the filter since we cannot tell whether the
-                * pending delete has already been processed by the PF.
-                * A duplicate add is harmless.
-                */
+               /* DEL not yet sent to PF, cancel it */
+               f->state = IAVF_VLAN_ACTIVE;
+       } else if (f->state == IAVF_VLAN_REMOVING) {
+               /* DEL already sent to PF, re-add after completion */
                f->state = IAVF_VLAN_ADD;
                iavf_schedule_aq_request(adapter,
                                         IAVF_FLAG_AQ_ADD_VLAN_FILTER);
@@ -791,11 +791,14 @@ static void iavf_del_vlan(struct iavf_adapter *adapter, struct iavf_vlan vlan)
                        list_del(&f->list);
                        kfree(f);
                        adapter->num_vlan_filters--;
-               } else {
+               } else if (f->state != IAVF_VLAN_REMOVING) {
                        f->state = IAVF_VLAN_REMOVE;
                        iavf_schedule_aq_request(adapter,
                                                 IAVF_FLAG_AQ_DEL_VLAN_FILTER);
                }
+               /* If REMOVING, DEL is already sent to PF; completion
+                * handler will free the filter when PF confirms.
+                */
        }
 
        spin_unlock_bh(&adapter->mac_vlan_list_lock);
index 4f197d908124e68b37222249dc6312724c4c826c..93ca79c3e3b535d1a6f30316ae015b4de57f8fb4 100644 (file)
@@ -948,12 +948,10 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
 
                vvfl->vsi_id = adapter->vsi_res->vsi_id;
                vvfl->num_elements = count;
-               list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
+               list_for_each_entry(f, &adapter->vlan_filter_list, list) {
                        if (f->state == IAVF_VLAN_REMOVE) {
                                vvfl->vlan_id[i] = f->vlan.vid;
-                               list_del(&f->list);
-                               kfree(f);
-                               adapter->num_vlan_filters--;
+                               f->state = IAVF_VLAN_REMOVING;
                                i++;
                                if (i == count)
                                        break;
@@ -990,7 +988,7 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
 
                vvfl_v2->vport_id = adapter->vsi_res->vsi_id;
                vvfl_v2->num_elements = count;
-               list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
+               list_for_each_entry(f, &adapter->vlan_filter_list, list) {
                        if (f->state == IAVF_VLAN_REMOVE) {
                                struct virtchnl_vlan_supported_caps *filtering_support =
                                        &adapter->vlan_v2_caps.filtering.filtering_support;
@@ -1005,9 +1003,7 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
                                vlan->tci = f->vlan.vid;
                                vlan->tpid = f->vlan.tpid;
 
-                               list_del(&f->list);
-                               kfree(f);
-                               adapter->num_vlan_filters--;
+                               f->state = IAVF_VLAN_REMOVING;
                                i++;
                                if (i == count)
                                        break;
@@ -2370,10 +2366,6 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                        ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
                        wake_up(&adapter->vc_waitqueue);
                        break;
-               case VIRTCHNL_OP_DEL_VLAN:
-                       dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n",
-                               iavf_stat_str(&adapter->hw, v_retval));
-                       break;
                case VIRTCHNL_OP_DEL_ETH_ADDR:
                        dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n",
                                iavf_stat_str(&adapter->hw, v_retval));
@@ -2895,6 +2887,27 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                spin_unlock_bh(&adapter->mac_vlan_list_lock);
                }
                break;
+       case VIRTCHNL_OP_DEL_VLAN:
+       case VIRTCHNL_OP_DEL_VLAN_V2: {
+               struct iavf_vlan_filter *f, *ftmp;
+
+               spin_lock_bh(&adapter->mac_vlan_list_lock);
+               list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list,
+                                        list) {
+                       if (f->state == IAVF_VLAN_REMOVING) {
+                               if (v_retval) {
+                                       /* PF rejected DEL, keep filter */
+                                       f->state = IAVF_VLAN_ACTIVE;
+                               } else {
+                                       list_del(&f->list);
+                                       kfree(f);
+                                       adapter->num_vlan_filters--;
+                               }
+                       }
+               }
+               spin_unlock_bh(&adapter->mac_vlan_list_lock);
+               }
+               break;
        case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
                /* PF enabled vlan strip on this VF.
                 * Update netdev->features if needed to be in sync with ethtool.