Commit bbcbe4ed authored by Petr Oros's avatar Petr Oros Committed by Paolo Abeni
Browse files

iavf: wait for PF confirmation before removing VLAN filters



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: 968996c0 ("iavf: Fix VLAN_V2 addition/rejection")
Signed-off-by: default avatarPetr Oros <poros@redhat.com>
Reviewed-by: default avatarAleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: default avatarRafal Romanowski <rafal.romanowski@intel.com>
Reviewed-by: default avatarPrzemek Kitszel <przemyslaw.kitszel@intel.com>
Signed-off-by: default avatarJacob 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: default avatarPaolo Abeni <pabeni@redhat.com>
parent f2ce65b9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 {
+8 −5
Original line number Diff line number Diff line
@@ -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);
+25 −12
Original line number Diff line number Diff line
@@ -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.