[PATCH RFC iwl-next 2/4] iavf: stop removing VLAN filters from PF on interface down

From: Petr Oros

Date: Mon Mar 02 2026 - 06:46:12 EST


When a VF goes down, the driver currently sends DEL_VLAN to the PF for
every VLAN filter (ACTIVE -> DISABLE -> send DEL -> INACTIVE), then
re-adds them all on UP (INACTIVE -> ADD -> send ADD -> ADDING ->
ACTIVE). This round-trip is unnecessary because:

1. The PF disables the VF's queues via VIRTCHNL_OP_DISABLE_QUEUES,
which already prevents all RX/TX traffic regardless of VLAN filter
state.

2. The VLAN filters remaining in PF HW while the VF is down is
harmless - packets matching those filters have nowhere to go with
queues disabled.

3. The DEL+ADD cycle during down/up creates race windows where the
VLAN filter list is incomplete. With spoofcheck enabled, the PF
enables TX VLAN filtering on the first non-zero VLAN add, blocking
traffic for any VLANs not yet re-added.

Remove the entire DISABLE/INACTIVE state machinery:
- Remove IAVF_VLAN_DISABLE and IAVF_VLAN_INACTIVE enum values
- Remove iavf_restore_filters() and its call from iavf_open()
- Remove VLAN filter handling from iavf_clear_mac_vlan_filters(),
rename it to iavf_clear_mac_filters()
- Remove DEL_VLAN_FILTER scheduling from iavf_down()
- Remove all DISABLE/INACTIVE handling from iavf_del_vlans()

VLAN filters now stay ACTIVE across down/up cycles. Only explicit
user removal (ndo_vlan_rx_kill_vid) or PF/VF reset triggers VLAN
filter deletion/re-addition.

Signed-off-by: Petr Oros <poros@xxxxxxxxxx>
---
drivers/net/ethernet/intel/iavf/iavf.h | 6 +--
drivers/net/ethernet/intel/iavf/iavf_main.c | 39 ++-----------------
.../net/ethernet/intel/iavf/iavf_virtchnl.c | 33 +++-------------
3 files changed, 12 insertions(+), 66 deletions(-)

diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
index 8e6db72828ae14..1ad00690622c8e 100644
--- a/drivers/net/ethernet/intel/iavf/iavf.h
+++ b/drivers/net/ethernet/intel/iavf/iavf.h
@@ -159,10 +159,8 @@ enum iavf_vlan_state_t {
IAVF_VLAN_INVALID,
IAVF_VLAN_ADD, /* filter needs to be added */
IAVF_VLAN_ADDING, /* ADD sent to PF, waiting for response */
- IAVF_VLAN_ACTIVE, /* filter is accepted by PF */
- IAVF_VLAN_DISABLE, /* filter needs to be deleted by PF, then marked INACTIVE */
- IAVF_VLAN_INACTIVE, /* filter is inactive, we are in IFF_DOWN */
- IAVF_VLAN_REMOVE, /* filter needs to be removed from list */
+ IAVF_VLAN_ACTIVE, /* PF confirmed, filter is in HW */
+ IAVF_VLAN_REMOVE, /* filter queued for DEL from PF */
};

struct iavf_vlan_filter {
diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c
index bceaf4b1b85d5f..f1ab68f5f4050b 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_main.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
@@ -823,27 +823,6 @@ static void iavf_del_vlan(struct iavf_adapter *adapter, struct iavf_vlan vlan)
spin_unlock_bh(&adapter->mac_vlan_list_lock);
}

-/**
- * iavf_restore_filters
- * @adapter: board private structure
- *
- * Restore existing non MAC filters when VF netdev comes back up
- **/
-static void iavf_restore_filters(struct iavf_adapter *adapter)
-{
- struct iavf_vlan_filter *f;
-
- /* re-add all VLAN filters */
- spin_lock_bh(&adapter->mac_vlan_list_lock);
-
- list_for_each_entry(f, &adapter->vlan_filter_list, list) {
- if (f->state == IAVF_VLAN_INACTIVE)
- f->state = IAVF_VLAN_ADD;
- }
-
- spin_unlock_bh(&adapter->mac_vlan_list_lock);
- adapter->aq_required |= IAVF_FLAG_AQ_ADD_VLAN_FILTER;
-}

/**
* iavf_get_num_vlans_added - get number of VLANs added
@@ -1262,13 +1241,12 @@ static void iavf_up_complete(struct iavf_adapter *adapter)
}

/**
- * iavf_clear_mac_vlan_filters - Remove mac and vlan filters not sent to PF
- * yet and mark other to be removed.
+ * iavf_clear_mac_filters - Remove MAC filters not sent to PF yet and mark
+ * others to be removed.
* @adapter: board private structure
**/
-static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter)
+static void iavf_clear_mac_filters(struct iavf_adapter *adapter)
{
- struct iavf_vlan_filter *vlf, *vlftmp;
struct iavf_mac_filter *f, *ftmp;

spin_lock_bh(&adapter->mac_vlan_list_lock);
@@ -1287,11 +1265,6 @@ static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter)
}
}

- /* disable all VLAN filters */
- list_for_each_entry_safe(vlf, vlftmp, &adapter->vlan_filter_list,
- list)
- vlf->state = IAVF_VLAN_DISABLE;
-
spin_unlock_bh(&adapter->mac_vlan_list_lock);
}

@@ -1387,7 +1360,7 @@ void iavf_down(struct iavf_adapter *adapter)
iavf_napi_disable_all(adapter);
iavf_irq_disable(adapter);

- iavf_clear_mac_vlan_filters(adapter);
+ iavf_clear_mac_filters(adapter);
iavf_clear_cloud_filters(adapter);
iavf_clear_fdir_filters(adapter);
iavf_clear_adv_rss_conf(adapter);
@@ -1404,8 +1377,6 @@ void iavf_down(struct iavf_adapter *adapter)
*/
if (!list_empty(&adapter->mac_filter_list))
adapter->aq_required |= IAVF_FLAG_AQ_DEL_MAC_FILTER;
- if (!list_empty(&adapter->vlan_filter_list))
- adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER;
if (!list_empty(&adapter->cloud_filter_list))
adapter->aq_required |= IAVF_FLAG_AQ_DEL_CLOUD_FILTER;
if (!list_empty(&adapter->fdir_list_head))
@@ -4487,8 +4458,6 @@ static int iavf_open(struct net_device *netdev)
iavf_add_filter(adapter, adapter->hw.mac.addr);
spin_unlock_bh(&adapter->mac_vlan_list_lock);

- /* Restore filters that were removed with IFF_DOWN */
- iavf_restore_filters(adapter);
iavf_restore_fdir_filters(adapter);

iavf_configure(adapter);
diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
index 5114934fe81fa6..d62c0d6394149e 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
@@ -911,22 +911,12 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
spin_lock_bh(&adapter->mac_vlan_list_lock);

list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
- /* since VLAN capabilities are not allowed, we dont want to send
- * a VLAN delete request because it will most likely fail and
- * create unnecessary errors/noise, so just free the VLAN
- * filters marked for removal to enable bailing out before
- * sending a virtchnl message
- */
if (f->state == IAVF_VLAN_REMOVE &&
!VLAN_FILTERING_ALLOWED(adapter)) {
list_del(&f->list);
kfree(f);
adapter->num_vlan_filters--;
- } else if (f->state == IAVF_VLAN_DISABLE &&
- !VLAN_FILTERING_ALLOWED(adapter)) {
- f->state = IAVF_VLAN_INACTIVE;
- } else if (f->state == IAVF_VLAN_REMOVE ||
- f->state == IAVF_VLAN_DISABLE) {
+ } else if (f->state == IAVF_VLAN_REMOVE) {
count++;
}
}
@@ -959,13 +949,7 @@ 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) {
- if (f->state == IAVF_VLAN_DISABLE) {
- vvfl->vlan_id[i] = f->vlan.vid;
- f->state = IAVF_VLAN_INACTIVE;
- i++;
- if (i == count)
- break;
- } else if (f->state == IAVF_VLAN_REMOVE) {
+ if (f->state == IAVF_VLAN_REMOVE) {
vvfl->vlan_id[i] = f->vlan.vid;
list_del(&f->list);
kfree(f);
@@ -1007,8 +991,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) {
- if (f->state == IAVF_VLAN_DISABLE ||
- f->state == IAVF_VLAN_REMOVE) {
+ if (f->state == IAVF_VLAN_REMOVE) {
struct virtchnl_vlan_supported_caps *filtering_support =
&adapter->vlan_v2_caps.filtering.filtering_support;
struct virtchnl_vlan *vlan;
@@ -1022,13 +1005,9 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
vlan->tci = f->vlan.vid;
vlan->tpid = f->vlan.tpid;

- if (f->state == IAVF_VLAN_DISABLE) {
- f->state = IAVF_VLAN_INACTIVE;
- } else {
- list_del(&f->list);
- kfree(f);
- adapter->num_vlan_filters--;
- }
+ list_del(&f->list);
+ kfree(f);
+ adapter->num_vlan_filters--;
i++;
if (i == count)
break;
--
2.52.0