Netdev List
 help / color / mirror / Atom feed
* [PATCH net 0/4][pull request] Fix i40e/ice/iavf VF bonding after netdev lock changes
@ 2026-08-21 20:45 Tony Nguyen
  2026-08-21 20:45 ` [PATCH net 1/4] iavf: return EBUSY if reset in progress or not ready during MAC change Tony Nguyen
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Tony Nguyen @ 2026-08-21 20:45 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Tony Nguyen, przemyslaw.kitszel, jacob.e.keller,
	aleksandr.loktionov, sdf, horms

Jose Ignacio Tornos Martinez says:

This series fixes VF bonding failures introduced by commit ad7c7b2172c3
("net: hold netdev instance lock during sysfs operations").

When adding VFs to a bond immediately after setting trust mode, MAC
address changes fail with -EAGAIN, preventing bonding setup. This
affects both i40e (700-series) and ice (800-series) Intel NICs.

The core issue is lock contention: iavf_set_mac() is now called with the
netdev lock held and waits for MAC change completion while holding it.
However, both the watchdog task that sends the request and the adminq_task
that processes PF responses also need this lock, creating a deadlock where
neither can run, causing timeouts.

Additionally, setting VF trust triggers an unnecessary ~10 second VF reset
in i40e driver that delays bonding setup, even though filter
synchronization happens naturally during normal VF operation. For ice
driver, the delay is not so big, but in the same way the operation is not
necessary.

This series:
1. Adds safety guard to prevent MAC changes during reset or early
   initialization (before VF is ready)
2. Eliminates unnecessary VF reset when setting trust in i40e (reset only
   if revoking trust and VF has advanced features configured).
3. Fixes lock contention by polling admin queue synchronously
4. Eliminates unnecessary VF reset when setting trust in ice, (reset only
   if revoking trust and VF has advanced features configured).

The key fix (patch 3/4) implements a synchronous MAC change operation
similar to the approach used for ndo_change_mtu deadlock fix:
https://lore.kernel.org/intel-wired-lan/20260211191855.1532226-1-poros@redhat.com/
Instead of scheduling work and waiting, it:

- Sends the virtchnl message directly (not via watchdog)
- Polls the admin queue hardware directly for responses
- Processes all messages inline (including non-MAC messages)
- Returns when complete or times out

This allows the operation to complete synchronously while holding
netdev_lock, without relying on watchdog or adminq_task.

The function can sleep for up to 2.5 seconds polling hardware, but this
is acceptable since netdev_lock is per-device and only serializes
operations on the same interface.

Testing shows VF bonding now works reliably in ~5 seconds vs 15+ seconds
before (i40e), without timeouts or errors (i40e and ice).

Tested on Intel 700-series (i40e) and 800-series (ice) dual-port NICs
with iavf driver.

Thanks to Jan Tluka <jtluka@redhat.com> and Yuying Ma <yuma@redhat.com> for
reporting the issues.
---
These patches are split off from this submission:
https://lore.kernel.org/netdev/20260804222205.1580328-1-anthony.l.nguyen@intel.com/

Which had comments from Sashiko; responses to each patch are linked below.

From patch 1 response:
All the comments below fall into pre-existing issues, concerns already
addressed in previous versions, out-of-scope items, or extreme edge
cases. No code changes are considered necessary for a new version.

Patch 1: https://lore.kernel.org/netdev/20260812065650.10326-1-jtornosm@redhat.com/
Patch 2: https://lore.kernel.org/netdev/20260812065955.10460-1-jtornosm@redhat.com/
Patch 3: https://lore.kernel.org/netdev/20260812070236.10559-1-jtornosm@redhat.com/
Patch 4: https://lore.kernel.org/netdev/20260812070502.10679-1-jtornosm@redhat.com/

IWL: https://lore.kernel.org/intel-wired-lan/20260623101800.991293-1-jtornosm@redhat.com/

The following are changes since commit 746fc0787f616da418ffc04a110296fe95d53491:
  net: usb: cdc_ncm: add Apple MacBook Pro USB product ID 0x1902
and are available in the git repository at:
  git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue 40GbE

Jose Ignacio Tornos Martinez (4):
  iavf: return EBUSY if reset in progress or not ready during MAC change
  i40e: skip unnecessary VF reset when setting trust
  iavf: send MAC change request synchronously
  ice: skip unnecessary VF reset when setting trust

 .../ethernet/intel/i40e/i40e_virtchnl_pf.c    | 38 +++++--
 drivers/net/ethernet/intel/iavf/iavf.h        | 11 ++-
 drivers/net/ethernet/intel/iavf/iavf_main.c   | 88 +++++++++++++----
 .../net/ethernet/intel/iavf/iavf_virtchnl.c   | 99 +++++++++++++++++--
 drivers/net/ethernet/intel/ice/ice_sriov.c    | 35 ++++++-
 5 files changed, 226 insertions(+), 45 deletions(-)

-- 
2.47.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH net 1/4] iavf: return EBUSY if reset in progress or not ready during MAC change
  2026-08-21 20:45 [PATCH net 0/4][pull request] Fix i40e/ice/iavf VF bonding after netdev lock changes Tony Nguyen
@ 2026-08-21 20:45 ` Tony Nguyen
  2026-08-24 19:01   ` Jakub Kicinski
  2026-08-21 20:45 ` [PATCH net 2/4] i40e: skip unnecessary VF reset when setting trust Tony Nguyen
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Tony Nguyen @ 2026-08-21 20:45 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Jose Ignacio Tornos Martinez, anthony.l.nguyen,
	przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov, sdf,
	horms, Rafal Romanowski

From: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

When a MAC address change is requested while the VF is resetting or still
initializing, return -EBUSY immediately instead of attempting the
operation.

Additionally, during early initialization states (before __IAVF_DOWN),
the PF may be slow to respond to MAC change requests, causing long
delays. Only allow MAC changes once the VF reaches __IAVF_DOWN state or
later, when the watchdog is running and the VF is ready for operations.

After commit ad7c7b2172c3 ("net: hold netdev instance lock
during sysfs operations"), MAC changes are called with the netdev lock
held, so we should not wait with the lock held during reset or
initialization. This allows the caller to retry or handle the busy state
appropriately without blocking other operations.

Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/iavf/iavf_main.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c
index 29b8403a066b..630388e9d28c 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_main.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
@@ -1042,6 +1042,9 @@ static int iavf_set_mac(struct net_device *netdev, void *p)
 	struct sockaddr *addr = p;
 	int ret;
 
+	if (iavf_is_reset_in_progress(adapter) || adapter->state < __IAVF_DOWN)
+		return -EBUSY;
+
 	if (!is_valid_ether_addr(addr->sa_data))
 		return -EADDRNOTAVAIL;
 
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net 2/4] i40e: skip unnecessary VF reset when setting trust
  2026-08-21 20:45 [PATCH net 0/4][pull request] Fix i40e/ice/iavf VF bonding after netdev lock changes Tony Nguyen
  2026-08-21 20:45 ` [PATCH net 1/4] iavf: return EBUSY if reset in progress or not ready during MAC change Tony Nguyen
@ 2026-08-21 20:45 ` Tony Nguyen
  2026-08-24 19:01   ` Jakub Kicinski
  2026-08-21 20:45 ` [PATCH net 3/4] iavf: send MAC change request synchronously Tony Nguyen
  2026-08-21 20:45 ` [PATCH net 4/4] ice: skip unnecessary VF reset when setting trust Tony Nguyen
  3 siblings, 1 reply; 9+ messages in thread
From: Tony Nguyen @ 2026-08-21 20:45 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Jose Ignacio Tornos Martinez, anthony.l.nguyen,
	przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov, sdf,
	horms, Rafal Romanowski

From: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

The current implementation triggers a VF reset when changing the trust
setting, causing a ~10 second delay during bonding setup.

In all the cases, the reset causes a ~10 second delay during which:
- VF must reinitialize completely
- Any in-progress operations (like bonding enslave) fail with timeouts
- VF is unavailable

When granting trust, no reset is needed - we can just set the capability
flag to allow privileged operations.

When revoking trust, we only need to reset (conservative approach) if
the VF has actually configured advanced features that require cleanup
(ADQ/cloud filters, promiscuous mode). For VFs in a clean state, we can
safely change the trust setting without the disruptive reset.

When we don't reset, we manually handle capability flag via helper
function, eliminating the delay.

Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 .../ethernet/intel/i40e/i40e_virtchnl_pf.c    | 38 ++++++++++++++-----
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index a26c3d47ec15..0cc434b26eb8 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -4943,6 +4943,23 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
 	return ret;
 }
 
+/**
+ * i40e_setup_vf_trust - Enable/disable VF trust mode without reset
+ * @vf: VF to configure
+ * @setting: trust setting
+ *
+ * Update VF flags when changing trust without performing a VF reset.
+ * This is only called when it's safe to skip the reset (VF has no advanced
+ * features configured that need cleanup).
+ */
+static void i40e_setup_vf_trust(struct i40e_vf *vf, bool setting)
+{
+	if (setting)
+		set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
+	else
+		clear_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
+}
+
 /**
  * i40e_ndo_set_vf_trust
  * @netdev: network interface device structure of the pf
@@ -4987,19 +5004,20 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
 	set_bit(__I40E_MACVLAN_SYNC_PENDING, pf->state);
 	pf->vsi[vf->lan_vsi_idx]->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
 
-	i40e_vc_reset_vf(vf, true);
+	/* Reset only if revoking trust and VF has advanced features configured */
+	if (!setting &&
+	    (vf->adq_enabled || vf->num_cloud_filters > 0 ||
+	     test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states) ||
+	     test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states))) {
+		i40e_vc_reset_vf(vf, true);
+		i40e_del_all_cloud_filters(vf);
+	} else {
+		i40e_setup_vf_trust(vf, setting);
+	}
+
 	dev_info(&pf->pdev->dev, "VF %u is now %strusted\n",
 		 vf_id, setting ? "" : "un");
 
-	if (vf->adq_enabled) {
-		if (!vf->trusted) {
-			dev_info(&pf->pdev->dev,
-				 "VF %u no longer Trusted, deleting all cloud filters\n",
-				 vf_id);
-			i40e_del_all_cloud_filters(vf);
-		}
-	}
-
 out:
 	clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
 	return ret;
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net 3/4] iavf: send MAC change request synchronously
  2026-08-21 20:45 [PATCH net 0/4][pull request] Fix i40e/ice/iavf VF bonding after netdev lock changes Tony Nguyen
  2026-08-21 20:45 ` [PATCH net 1/4] iavf: return EBUSY if reset in progress or not ready during MAC change Tony Nguyen
  2026-08-21 20:45 ` [PATCH net 2/4] i40e: skip unnecessary VF reset when setting trust Tony Nguyen
@ 2026-08-21 20:45 ` Tony Nguyen
  2026-08-24 19:01   ` Jakub Kicinski
  2026-08-21 20:45 ` [PATCH net 4/4] ice: skip unnecessary VF reset when setting trust Tony Nguyen
  3 siblings, 1 reply; 9+ messages in thread
From: Tony Nguyen @ 2026-08-21 20:45 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Jose Ignacio Tornos Martinez, anthony.l.nguyen,
	przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov, sdf,
	horms, stable, Rafal Romanowski

From: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

After commit ad7c7b2172c3 ("net: hold netdev instance lock during sysfs
operations"), iavf_set_mac() is called with the netdev instance lock
already held.

The function queues a MAC address change request via
iavf_replace_primary_mac() and then waits for completion. However, in
the current flow, the actual virtchnl message is sent by the watchdog
task, which also needs to acquire the netdev lock to run. Additionally,
the adminq_task which processes virtchnl responses also needs the netdev
lock.

This creates a deadlock scenario:
1. iavf_set_mac() holds netdev lock and waits for MAC change
2. Watchdog needs netdev lock to send the request -> blocked
3. Even if request is sent, adminq_task needs netdev lock to process
   PF response -> blocked
4. MAC change times out after 2.5 seconds
5. iavf_set_mac() returns -EAGAIN

This particularly affects VFs during bonding setup when multiple VFs are
enslaved in quick succession.

Fix by implementing a synchronous MAC change operation similar to the
approach used in commit fdadbf6e84c4 ("iavf: fix incorrect reset handling
in callbacks").

The solution:
1. Send the virtchnl ADD_ETH_ADDR message directly (not via watchdog)
2. Poll the admin queue hardware directly for responses
3. Process all received messages (including non-MAC messages)
4. Return when MAC change completes or times out

A new generic function iavf_poll_virtchnl_response() is introduced that
can be reused for any future synchronous virtchnl operations. It takes a
callback to check completion, allowing flexible condition checking.

This allows the operation to complete synchronously while holding
netdev_lock, without relying on watchdog or adminq_task. The function
can sleep for up to 2.5 seconds polling hardware, but this is acceptable
since netdev_lock is per-device and only serializes operations on the
same interface.

To support this, change iavf_add_ether_addrs() to return an error code
instead of void, allowing callers to detect failures. Additionally,
export iavf_mac_add_reject() to enable proper rollback on local failures
(timeouts, send errors) - PF rejections are already handled automatically
by iavf_virtchnl_completion().

Remove vc_waitqueue entirely because iavf_set_mac was the only waiter on
this waitqueue and after the changes it is not needed.

Fixes: ad7c7b2172c3 ("net: hold netdev instance lock during sysfs operations")
cc: stable@vger.kernel.org
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/iavf/iavf.h        | 11 ++-
 drivers/net/ethernet/intel/iavf/iavf_main.c   | 85 ++++++++++++----
 .../net/ethernet/intel/iavf/iavf_virtchnl.c   | 99 +++++++++++++++++--
 3 files changed, 165 insertions(+), 30 deletions(-)

diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
index dc31202b2a94..8c45536fd502 100644
--- a/drivers/net/ethernet/intel/iavf/iavf.h
+++ b/drivers/net/ethernet/intel/iavf/iavf.h
@@ -259,7 +259,6 @@ struct iavf_adapter {
 	struct work_struct adminq_task;
 	struct work_struct finish_config;
 	wait_queue_head_t down_waitqueue;
-	wait_queue_head_t vc_waitqueue;
 	struct iavf_q_vector *q_vectors;
 	struct list_head vlan_filter_list;
 	int num_vlan_filters;
@@ -588,8 +587,9 @@ void iavf_configure_queues(struct iavf_adapter *adapter);
 void iavf_enable_queues(struct iavf_adapter *adapter);
 void iavf_disable_queues(struct iavf_adapter *adapter);
 void iavf_map_queues(struct iavf_adapter *adapter);
-void iavf_add_ether_addrs(struct iavf_adapter *adapter);
+int iavf_add_ether_addrs(struct iavf_adapter *adapter);
 void iavf_del_ether_addrs(struct iavf_adapter *adapter);
+void iavf_mac_add_reject(struct iavf_adapter *adapter);
 void iavf_add_vlans(struct iavf_adapter *adapter);
 void iavf_del_vlans(struct iavf_adapter *adapter);
 void iavf_set_promiscuous(struct iavf_adapter *adapter);
@@ -606,6 +606,13 @@ void iavf_disable_vlan_stripping(struct iavf_adapter *adapter);
 void iavf_virtchnl_completion(struct iavf_adapter *adapter,
 			      enum virtchnl_ops v_opcode,
 			      enum iavf_status v_retval, u8 *msg, u16 msglen);
+int iavf_poll_virtchnl_response(struct iavf_adapter *adapter,
+				struct iavf_arq_event_info *event,
+				bool (*condition)(struct iavf_adapter *adapter,
+						  const void *data,
+						  enum virtchnl_ops v_op),
+				const void *cond_data,
+				unsigned int timeout_ms);
 int iavf_config_rss(struct iavf_adapter *adapter);
 void iavf_cfg_queues_bw(struct iavf_adapter *adapter);
 void iavf_cfg_queues_quanta_size(struct iavf_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c
index 630388e9d28c..3fa288e3798a 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_main.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
@@ -1029,6 +1029,60 @@ static bool iavf_is_mac_set_handled(struct net_device *netdev,
 	return ret;
 }
 
+/**
+ * iavf_mac_change_done - Check if MAC change completed
+ * @adapter: board private structure
+ * @data: MAC address being checked (as const void *)
+ * @v_op: virtchnl opcode from processed message
+ *
+ * Callback for iavf_poll_virtchnl_response() to check if MAC change completed.
+ *
+ * Return: true if MAC change completed, false otherwise
+ */
+static bool iavf_mac_change_done(struct iavf_adapter *adapter,
+				 const void *data, enum virtchnl_ops v_op)
+{
+	const u8 *addr = data;
+
+	return iavf_is_mac_set_handled(adapter->netdev, addr);
+}
+
+/**
+ * iavf_set_mac_sync - Synchronously change MAC address
+ * @adapter: board private structure
+ * @addr: MAC address to set
+ *
+ * Send MAC change request to PF and poll admin queue for response.
+ * Caller must hold netdev_lock. This can sleep for up to 2.5 seconds.
+ * Event buffer is allocated before sending to avoid state mismatch if
+ * allocation fails after message is sent to PF.
+ *
+ * Return: 0 on success, negative on failure
+ */
+static int iavf_set_mac_sync(struct iavf_adapter *adapter, const u8 *addr)
+{
+	struct iavf_arq_event_info event;
+	int ret;
+
+	netdev_assert_locked(adapter->netdev);
+
+	event.buf_len = IAVF_MAX_AQ_BUF_SIZE;
+	event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL);
+	if (!event.msg_buf)
+		return -ENOMEM;
+
+	ret = iavf_add_ether_addrs(adapter);
+	if (ret)
+		goto out;
+
+	ret = iavf_poll_virtchnl_response(adapter, &event,
+					  iavf_mac_change_done, addr, 2500);
+
+out:
+	kfree(event.msg_buf);
+	return ret;
+}
+
 /**
  * iavf_set_mac - NDO callback to set port MAC address
  * @netdev: network interface device structure
@@ -1049,25 +1103,23 @@ static int iavf_set_mac(struct net_device *netdev, void *p)
 		return -EADDRNOTAVAIL;
 
 	ret = iavf_replace_primary_mac(adapter, addr->sa_data);
-
 	if (ret)
 		return ret;
 
-	ret = wait_event_interruptible_timeout(adapter->vc_waitqueue,
-					       iavf_is_mac_set_handled(netdev, addr->sa_data),
-					       msecs_to_jiffies(2500));
-
-	/* If ret < 0 then it means wait was interrupted.
-	 * If ret == 0 then it means we got a timeout.
-	 * else it means we got response for set MAC from PF,
-	 * check if netdev MAC was updated to requested MAC,
-	 * if yes then set MAC succeeded otherwise it failed return -EACCES
-	 */
-	if (ret < 0)
+	ret = iavf_set_mac_sync(adapter, addr->sa_data);
+	if (ret) {
+		/* Rollback only if send failed (message never reached PF).
+		 * Don't rollback on timeout (-EAGAIN) because the message was
+		 * sent and PF will eventually respond. When the response arrives,
+		 * iavf_virtchnl_completion() will handle rollback (on PF error)
+		 * or acceptance (on PF success) automatically.
+		 */
+		if (ret != -EAGAIN) {
+			iavf_mac_add_reject(adapter);
+			ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
+		}
 		return ret;
-
-	if (!ret)
-		return -EAGAIN;
+	}
 
 	if (!ether_addr_equal(netdev->dev_addr, addr->sa_data))
 		return -EACCES;
@@ -5397,9 +5449,6 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	/* Setup the wait queue for indicating transition to down status */
 	init_waitqueue_head(&adapter->down_waitqueue);
 
-	/* Setup the wait queue for indicating virtchannel events */
-	init_waitqueue_head(&adapter->vc_waitqueue);
-
 	INIT_LIST_HEAD(&adapter->ptp.aq_cmds);
 	init_waitqueue_head(&adapter->ptp.phc_time_waitqueue);
 	mutex_init(&adapter->ptp.aq_cmd_lock);
diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
index ec234cc8bd9d..e6b7e8f82c7c 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
@@ -2,6 +2,7 @@
 /* Copyright(c) 2013 - 2018 Intel Corporation. */
 
 #include <linux/net/intel/libie/rx.h>
+#include <net/netdev_lock.h>
 
 #include "iavf.h"
 #include "iavf_ptp.h"
@@ -555,20 +556,23 @@ iavf_set_mac_addr_type(struct virtchnl_ether_addr *virtchnl_ether_addr,
  * @adapter: adapter structure
  *
  * Request that the PF add one or more addresses to our filters.
- **/
-void iavf_add_ether_addrs(struct iavf_adapter *adapter)
+ *
+ * Return: 0 on success, negative on failure
+ */
+int iavf_add_ether_addrs(struct iavf_adapter *adapter)
 {
 	struct virtchnl_ether_addr_list *veal;
 	struct iavf_mac_filter *f;
 	int i = 0, count = 0;
 	bool more = false;
 	size_t len;
+	int ret;
 
 	if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
 		/* bail because we already have a command pending */
 		dev_err(&adapter->pdev->dev, "Cannot add filters, command %d pending\n",
 			adapter->current_op);
-		return;
+		return -EBUSY;
 	}
 
 	spin_lock_bh(&adapter->mac_vlan_list_lock);
@@ -580,7 +584,7 @@ void iavf_add_ether_addrs(struct iavf_adapter *adapter)
 	if (!count) {
 		adapter->aq_required &= ~IAVF_FLAG_AQ_ADD_MAC_FILTER;
 		spin_unlock_bh(&adapter->mac_vlan_list_lock);
-		return;
+		return 0;
 	}
 	adapter->current_op = VIRTCHNL_OP_ADD_ETH_ADDR;
 
@@ -594,8 +598,9 @@ void iavf_add_ether_addrs(struct iavf_adapter *adapter)
 
 	veal = kzalloc(len, GFP_ATOMIC);
 	if (!veal) {
+		adapter->current_op = VIRTCHNL_OP_UNKNOWN;
 		spin_unlock_bh(&adapter->mac_vlan_list_lock);
-		return;
+		return -ENOMEM;
 	}
 
 	veal->vsi_id = adapter->vsi_res->vsi_id;
@@ -615,8 +620,15 @@ void iavf_add_ether_addrs(struct iavf_adapter *adapter)
 
 	spin_unlock_bh(&adapter->mac_vlan_list_lock);
 
-	iavf_send_pf_msg(adapter, VIRTCHNL_OP_ADD_ETH_ADDR, (u8 *)veal, len);
+	ret = iavf_send_pf_msg(adapter, VIRTCHNL_OP_ADD_ETH_ADDR, (u8 *)veal, len);
 	kfree(veal);
+	if (ret) {
+		dev_err(&adapter->pdev->dev,
+			"Unable to send ADD_ETH_ADDR message to PF, error %d\n", ret);
+		adapter->current_op = VIRTCHNL_OP_UNKNOWN;
+	}
+
+	return ret;
 }
 
 /**
@@ -712,8 +724,8 @@ static void iavf_mac_add_ok(struct iavf_adapter *adapter)
  * @adapter: adapter structure
  *
  * Remove filters from list based on PF response.
- **/
-static void iavf_mac_add_reject(struct iavf_adapter *adapter)
+ */
+void iavf_mac_add_reject(struct iavf_adapter *adapter)
 {
 	struct net_device *netdev = adapter->netdev;
 	struct iavf_mac_filter *f, *ftmp;
@@ -2364,7 +2376,6 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
 			iavf_mac_add_reject(adapter);
 			/* restore administratively set MAC address */
 			ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
-			wake_up(&adapter->vc_waitqueue);
 			break;
 		case VIRTCHNL_OP_DEL_ETH_ADDR:
 			dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n",
@@ -2555,7 +2566,6 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
 			eth_hw_addr_set(netdev, adapter->hw.mac.addr);
 			netif_addr_unlock_bh(netdev);
 		}
-		wake_up(&adapter->vc_waitqueue);
 		break;
 	case VIRTCHNL_OP_GET_STATS: {
 		struct iavf_eth_stats *stats =
@@ -2950,3 +2960,72 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
 	} /* switch v_opcode */
 	adapter->current_op = VIRTCHNL_OP_UNKNOWN;
 }
+
+/**
+ * iavf_poll_virtchnl_response - Poll admin queue for virtchnl response
+ * @adapter: adapter structure
+ * @event: pre-allocated event buffer to use for polling
+ * @condition: callback to check if desired response received
+ * @cond_data: context data passed to condition callback
+ * @timeout_ms: maximum time to wait in milliseconds
+ *
+ * Polls the admin queue and processes all incoming virtchnl messages.
+ * After processing each valid message, calls the condition callback to check
+ * if the expected response has been received. The callback receives the opcode
+ * of the processed message to identify which response was received. Continues
+ * polling until the callback returns true or timeout expires.
+ *
+ * Caller must allocate event buffer before sending any messages to PF to avoid
+ * state mismatch if allocation fails after message is sent.
+ *
+ * Caller must hold netdev_lock. This can sleep for up to timeout_ms while
+ * polling hardware.
+ *
+ * Return: 0 on success (condition met), -EAGAIN on timeout, or error code
+ */
+int iavf_poll_virtchnl_response(struct iavf_adapter *adapter,
+				struct iavf_arq_event_info *event,
+				bool (*condition)(struct iavf_adapter *adapter,
+						  const void *data,
+						  enum virtchnl_ops v_op),
+				const void *cond_data,
+				unsigned int timeout_ms)
+{
+	struct iavf_hw *hw = &adapter->hw;
+	enum virtchnl_ops received_op;
+	unsigned long timeout;
+	int ret = -EAGAIN;
+	u16 pending = 0;
+	u32 v_retval;
+
+	netdev_assert_locked(adapter->netdev);
+
+	timeout = jiffies + msecs_to_jiffies(timeout_ms);
+	do {
+		if (!pending)
+			usleep_range(50, 75);
+
+		if (iavf_clean_arq_element(hw, event, &pending) == IAVF_SUCCESS) {
+			received_op = (enum virtchnl_ops)le32_to_cpu(event->desc.cookie_high);
+			if (received_op != VIRTCHNL_OP_UNKNOWN) {
+				v_retval = le32_to_cpu(event->desc.cookie_low);
+
+				iavf_virtchnl_completion(adapter, received_op,
+							 (enum iavf_status)v_retval,
+							 event->msg_buf, event->msg_len);
+
+				if (condition(adapter, cond_data, received_op)) {
+					ret = 0;
+					break;
+				}
+			}
+
+			memset(event->msg_buf, 0, IAVF_MAX_AQ_BUF_SIZE);
+
+			if (pending)
+				continue;
+		}
+	} while (time_before(jiffies, timeout));
+
+	return ret;
+}
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net 4/4] ice: skip unnecessary VF reset when setting trust
  2026-08-21 20:45 [PATCH net 0/4][pull request] Fix i40e/ice/iavf VF bonding after netdev lock changes Tony Nguyen
                   ` (2 preceding siblings ...)
  2026-08-21 20:45 ` [PATCH net 3/4] iavf: send MAC change request synchronously Tony Nguyen
@ 2026-08-21 20:45 ` Tony Nguyen
  2026-08-24 19:01   ` Jakub Kicinski
  3 siblings, 1 reply; 9+ messages in thread
From: Tony Nguyen @ 2026-08-21 20:45 UTC (permalink / raw)
  To: davem, kuba, pabeni, edumazet, andrew+netdev, netdev
  Cc: Jose Ignacio Tornos Martinez, anthony.l.nguyen,
	przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov, sdf,
	horms, Rafal Romanowski

From: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

Similar to the i40e fix, ice_set_vf_trust() unconditionally calls
ice_reset_vf() when the trust setting changes. While the delay is smaller
than i40e, this reset is still unnecessary in most cases.

When granting trust, no reset is needed - we can just set the capability
flag to allow privileged operations.

When revoking trust, we only need to reset (conservative approach) if
the VF has actually configured advanced features that require cleanup
(MAC LLDP filters, promiscuous mode). For VFs in a clean state, we can
safely change the trust setting without the disruptive reset.

When we do reset, we maintain the original ice pattern that has been
reliable in production: cleanup LLDP filters first, then set vf->trusted,
then reset. This ensures the privilege capability bit is handled correctly
during reset rebuild.

When we don't reset, we manually handle the capability flag via helper
function, eliminating the delay.

Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/ice/ice_sriov.c | 35 ++++++++++++++++++----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c
index e04de0215596..558937899818 100644
--- a/drivers/net/ethernet/intel/ice/ice_sriov.c
+++ b/drivers/net/ethernet/intel/ice/ice_sriov.c
@@ -1366,6 +1366,23 @@ int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
 	return __ice_set_vf_mac(ice_netdev_to_pf(netdev), vf_id, mac);
 }
 
+/**
+ * ice_setup_vf_trust - Enable/disable VF trust mode without reset
+ * @vf: VF to configure
+ * @setting: trust setting
+ *
+ * Update VF flags when changing trust without performing a VF reset.
+ * This is only called when it's safe to skip the reset (VF has no advanced
+ * features configured that need cleanup).
+ */
+static void ice_setup_vf_trust(struct ice_vf *vf, bool setting)
+{
+	if (setting)
+		set_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
+	else
+		clear_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
+}
+
 /**
  * ice_set_vf_trust
  * @netdev: network interface device structure
@@ -1401,11 +1418,19 @@ int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted)
 
 	mutex_lock(&vf->cfg_lock);
 
-	while (!trusted && vf->num_mac_lldp)
-		ice_vf_update_mac_lldp_num(vf, ice_get_vf_vsi(vf), false);
-
-	vf->trusted = trusted;
-	ice_reset_vf(vf, ICE_VF_RESET_NOTIFY);
+	/* Reset only if revoking trust and VF has advanced features configured */
+	if (!trusted &&
+	    (vf->num_mac_lldp > 0 ||
+	     test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) ||
+	     test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states))) {
+		while (vf->num_mac_lldp)
+			ice_vf_update_mac_lldp_num(vf, ice_get_vf_vsi(vf), false);
+		vf->trusted = trusted;
+		ice_reset_vf(vf, ICE_VF_RESET_NOTIFY);
+	} else {
+		vf->trusted = trusted;
+		ice_setup_vf_trust(vf, trusted);
+	}
 	dev_info(ice_pf_to_dev(pf), "VF %u is now %strusted\n",
 		 vf_id, trusted ? "" : "un");
 
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/4] iavf: return EBUSY if reset in progress or not ready during MAC change
  2026-08-21 20:45 ` [PATCH net 1/4] iavf: return EBUSY if reset in progress or not ready during MAC change Tony Nguyen
@ 2026-08-24 19:01   ` Jakub Kicinski
  0 siblings, 0 replies; 9+ messages in thread
From: Jakub Kicinski @ 2026-08-24 19:01 UTC (permalink / raw)
  To: anthony.l.nguyen
  Cc: Jakub Kicinski, davem, pabeni, edumazet, andrew+netdev, netdev,
	jtornosm, przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov,
	sdf, horms, stable, rafal.romanowski

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
iavf: return EBUSY if reset in progress or not ready during MAC change

This adds an early bail-out to iavf_set_mac() so that a MAC address change
returns -EBUSY when a reset is in progress or when the VF has not yet
reached __IAVF_DOWN.  The stated motivation is to avoid waiting inside the
ndo callback while the netdev instance lock is held.

No Link: tags present in the commit header.

A couple of questions below, one about the commit message and one about the
new check itself.

>     After commit ad7c7b2172c3 ("net: hold netdev instance lock
>     during sysfs operations"), MAC changes are called with the netdev lock
>     held, so we should not wait with the lock held during reset or
>     initialization.

[Severity: Low]
This paragraph describes a user-visible behaviour change caused by
ad7c7b2172c3, but there is no Fixes: tag on this commit.  Was that
intentional?

Worth noting that this commit does not actually remove the wait under the
netdev instance lock: after this patch iavf_set_mac() still calls

	ret = wait_event_interruptible_timeout(adapter->vc_waitqueue,
					       iavf_is_mac_set_handled(netdev, addr->sa_data),
					       msecs_to_jiffies(2500));

so a Fixes: ad7c7b2172c3 here would arguably be misleading.  The later
patch in the same series, "iavf: send MAC change request synchronously",
does carry Fixes: ad7c7b2172c3 and cc: stable, which looks like the right
place for the provenance.  Is that the intended split?

> diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c
> index 29b8403a066b..630388e9d28c 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf_main.c
> +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
> @@ -1042,6 +1042,9 @@ static int iavf_set_mac(struct net_device *netdev, void *p)
>  	struct sockaddr *addr = p;
>  	int ret;
>  
> +	if (iavf_is_reset_in_progress(adapter) || adapter->state < __IAVF_DOWN)
> +		return -EBUSY;
> +

[Severity: Low]
Is this check-then-act sequence racy against the reset scheduling path?

iavf_is_reset_in_progress() does plain reads of adapter->state and
adapter->flags:

	static bool iavf_is_reset_in_progress(struct iavf_adapter *adapter)
	{
		if (adapter->state == __IAVF_RESETTING ||
		    adapter->flags & (IAVF_FLAG_RESET_PENDING |
				      IAVF_FLAG_RESET_NEEDED))
			return true;

		return false;
	}

IAVF_FLAG_RESET_NEEDED can be set from a context that does not hold the
netdev instance lock:

dev_watchdog()
  netif_freeze_queues(dev);
  dev->netdev_ops->ndo_tx_timeout(dev, i);    /* timer context */
    iavf_tx_timeout()
      iavf_schedule_reset(adapter, IAVF_FLAG_RESET_NEEDED)
        adapter->flags |= flags;              /* non-atomic RMW */

net/sched/sch_generic.c only freezes the TX queues around ndo_tx_timeout(),
so there does not appear to be anything serializing that writer against
iavf_set_mac().  If the TX timeout fires just after the new test, does
iavf_set_mac() go on to the 2.5 s wait_event_interruptible_timeout() with
the netdev instance lock held anyway, i.e. exactly the case the check is
meant to reject?

The window is small and the fallback is the pre-patch behaviour (blocked
for up to 2.5 s, then -EAGAIN), so this looks like a best-effort guard
rather than a new failure mode.  Is that the intent, or should the state
and flags reads be done under the same serialization as the reset path
(and with READ_ONCE(), given the concurrent non-atomic |= on
adapter->flags)?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 2/4] i40e: skip unnecessary VF reset when setting trust
  2026-08-21 20:45 ` [PATCH net 2/4] i40e: skip unnecessary VF reset when setting trust Tony Nguyen
@ 2026-08-24 19:01   ` Jakub Kicinski
  0 siblings, 0 replies; 9+ messages in thread
From: Jakub Kicinski @ 2026-08-24 19:01 UTC (permalink / raw)
  To: anthony.l.nguyen
  Cc: Jakub Kicinski, davem, pabeni, edumazet, andrew+netdev, netdev,
	jtornosm, przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov,
	sdf, horms, stable, rafal.romanowski

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
i40e: skip unnecessary VF reset when setting trust

This changes i40e_ndo_set_vf_trust() so that the VF reset is only performed
when trust is being revoked and the VF has ADQ, cloud filters or
promiscuous mode configured. In the other cases a new helper,
i40e_setup_vf_trust(), just sets or clears
I40E_VIRTCHNL_VF_CAP_PRIVILEGE. A few questions below about the state that
the skipped reset used to clean up.

> diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
> index a26c3d47ec15..0cc434b26eb8 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
> @@ -4943,6 +4943,23 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
>  	return ret;
>  }
>  
> +/**
> + * i40e_setup_vf_trust - Enable/disable VF trust mode without reset
> + * @vf: VF to configure
> + * @setting: trust setting
> + *
> + * Update VF flags when changing trust without performing a VF reset.
> + * This is only called when it's safe to skip the reset (VF has no advanced
> + * features configured that need cleanup).
> + */
> +static void i40e_setup_vf_trust(struct i40e_vf *vf, bool setting)
> +{
> +	if (setting)
> +		set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
> +	else
> +		clear_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
> +}

[Severity: Medium]

This helper writes I40E_VIRTCHNL_VF_CAP_PRIVILEGE from the ndo (rtnl)
context. i40e_alloc_vf_res() derives the same bit from an unsynchronized
read of vf->trusted:

i40e_alloc_vf_res()
	if (vf->trusted)
		set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
	else
		clear_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);

That runs from i40e_reset_vf()->i40e_cleanup_reset_vf()->i40e_alloc_vf_res()
in service task context, and a VF can request a reset itself through
VIRTCHNL_OP_RESET_VF.

If that path reads vf->trusted and is preempted before the
set_bit()/clear_bit(), can i40e_ndo_set_vf_trust() update vf->trusted and
call i40e_setup_vf_trust() in between, so the stale write lands last? That
would leave vf->trusted true with the capability bit clear, or vf->trusted
false with the bit still set.

Before this patch the ndo always followed the vf->trusted update with
i40e_vc_reset_vf(), so the bit was recomputed from the final value of
vf->trusted. With the reset skipped, is anything left that reconciles the
two?

>  /**
>   * i40e_ndo_set_vf_trust
>   * @netdev: network interface device structure of the pf
> @@ -4987,19 +5004,20 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
>  	set_bit(__I40E_MACVLAN_SYNC_PENDING, pf->state);
>  	pf->vsi[vf->lan_vsi_idx]->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
>  
> -	i40e_vc_reset_vf(vf, true);
> +	/* Reset only if revoking trust and VF has advanced features configured */
> +	if (!setting &&
> +	    (vf->adq_enabled || vf->num_cloud_filters > 0 ||
> +	     test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states) ||
> +	     test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states))) {

[Severity: High]

Does this condition need to consider the MAC and VLAN filters that the VF
was only allowed to install because it was trusted?

Trust is checked at add time only. i40e_check_vf_permission():

	vf_trusted = test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
	...
	if (!vf_trusted && !is_multicast_ether_addr(addr) &&
	    vf->pf_set_mac && ...)
		return -EPERM;
	...
	if (!vf_trusted)
		mac_add_max = I40E_VC_MAX_MAC_ADDR_PER_VF;
	else
		mac_add_max = I40E_VC_MAX_MACVLAN_PER_TRUSTED_VF(...);

and i40e_vc_add_vlan_msg():

	if ((vf->num_vlan >= I40E_VC_MAX_VLAN_PER_VF) &&
	    !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) {

Nothing revalidates filters that are already installed when the privilege
bit is dropped.

Previously the revoke always went through
i40e_vc_reset_vf()->i40e_reset_vf()->i40e_cleanup_reset_vf(), which calls
i40e_free_vf_res():

i40e_free_vf_res()
	if (vf->lan_vsi_idx) {
		i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]);

so every MAC/VLAN filter was purged, and i40e_cleanup_reset_vf() also set
vf->num_vlan = 0.

A VF that used only the privileged MAC/VLAN allowances has
adq_enabled == false, num_cloud_filters == 0 and both promisc bits clear,
so it takes the else branch here and only the capability bit is cleared.
Can such a VF keep receiving on MAC/VLAN combinations an untrusted VF may
never request, including a MAC that overrides the administratively set
vf->default_lan_addr, while ip link reports trust off?

Is the requested MACVLAN resync enough to cover this? Following
i40e_sync_vsi_filters()->i40e_correct_vf_mac_vlan_filters() in
i40e_main.c:

	new_vlan = i40e_get_vf_new_vlan(vsi, NULL, f, vlan_filters, trusted);
	if (new_vlan != f->vlan) { ... }

that only re-derives I40E_VLAN_ANY versus 0 for existing filters, it does
not delete filters that exceed the untrusted quota.

As a second effect, vf->num_vlan and the active MAC filter count stay above
the untrusted limits, so does every later VLAN/MAC add from that VF get
rejected until an unrelated reset happens?

[Severity: Medium]

Can this sample of VF-controlled state race with the virtchnl handlers that
write it? i40e_vc_process_vf_msg() is called from
i40e_clean_adminq_subtask() in i40e_main.c:

	case i40e_aqc_opc_send_msg_to_pf:
		ret = i40e_vc_process_vf_msg(pf, ...);

which runs in service task context without rtnl_lock and without taking
__I40E_VIRTCHNL_OP_PENDING, so it does not exclude this ndo.

CPU1 in i40e_vc_config_promiscuous_mode_msg() passes the gate and starts
the admin queue work:

	if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { ... }
	...
	aq_ret = i40e_config_vf_promiscuous_mode(vf, info->vsi_id,
						 allmulti, alluni);
	if (aq_ret)
		goto err_out;
	... test_and_set_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)

CPU0 then runs i40e_ndo_set_vf_trust(), sees both promisc bits still clear
(they are set only after the admin queue sequence), takes the else branch,
clears the privilege bit and skips the reset. CPU1 finishes and sets
I40E_VF_STATE_UC_PROMISC with promiscuous mode programmed in hardware.

Does that leave an untrusted VF promiscuous indefinitely? The same ordering
appears in i40e_vc_add_cloud_filter(), where

	hlist_add_head(&cfilter->cloud_node, &vf->cloud_filter_list);
	vf->num_cloud_filters++;

happens after the filter is programmed, and in i40e_vc_add_qch_msg() which
sets vf->adq_enabled last.

> +		i40e_vc_reset_vf(vf, true);

[Severity: Medium]

This is a pre-existing issue and not introduced by this patch, but in this
branch the capability bit and the hardware promiscuous teardown are only
side effects of the reset, and i40e_vc_reset_vf() is void and can return
having done nothing:

i40e_vc_reset_vf()
	if (test_bit(__I40E_VFS_RELEASING, pf->state))
		return;
	if (i40e_reset_vf(vf, false))
		return;

i40e_reset_vf() also returns true immediately when __I40E_VF_RESETS_DISABLED
or __I40E_VF_DISABLE is set, and after 20 attempts the helper only warns.

In those cases vf->trusted is already false and the driver prints "VF %u is
now untrusted", but the bit consulted by i40e_check_vf_permission(),
i40e_vc_config_promiscuous_mode_msg(), i40e_vc_add_vlan_msg() and
i40e_vc_add_cloud_filter() is still set, since i40e_setup_vf_trust() is
only called from the else branch. i40e_ndo_get_vf_config() reports
ivi->trusted = vf->trusted, so the two can disagree.

Would calling i40e_setup_vf_trust(vf, setting) unconditionally, before the
branch, make both artifacts deterministic?

> +		i40e_del_all_cloud_filters(vf);

[Severity: Medium]

This ordering is pre-existing and not introduced by this patch, the
baseline also called i40e_vc_reset_vf() before i40e_del_all_cloud_filters()
in the "if (vf->adq_enabled && !vf->trusted)" block, but the restructuring
keeps it.

The reset just above releases the ADQ channel VSIs and zeroes their ids in
i40e_free_vf_res():

	if (j)
		i40e_vsi_release(pf->vsi[vf->ch[j].vsi_idx]);
	vf->ch[j].vsi_idx = 0;
	vf->ch[j].vsi_id = 0;

and i40e_alloc_vf_res() re-creates them with newly assigned seids. The
teardown then looks the VSI up by the recorded seid in
i40e_find_vsi_from_seid():

	for (i = 0; i < vf->num_tc ; i++) {
		vsi = i40e_find_vsi_from_id(pf, vf->ch[i].vsi_id);
		if (vsi && vsi->seid == seid)
			return vsi;
	}

If the seid changed, i40e_del_all_cloud_filters() takes the error path:

	vsi = i40e_find_vsi_from_seid(vf, cfilter->seid);
	if (!vsi) {
		dev_err(... "can't delete cloud filter\n" ...);
		continue;
	}

so the hlist_del(), the kfree(cfilter) and the vf->num_cloud_filters
decrement are all skipped. Does this leak the struct i40e_cloud_filter
allocations? Nothing else walks vf->cloud_filter_list, and with
vf->num_cloud_filters stuck non-zero the VF also takes the reset branch
here forever. Would deleting the cloud filters before triggering the reset
be the right order?

> +	} else {
> +		i40e_setup_vf_trust(vf, setting);
> +	}
> +
>  	dev_info(&pf->pdev->dev, "VF %u is now %strusted\n",
>  		 vf_id, setting ? "" : "un");
>  
> -	if (vf->adq_enabled) {
> -		if (!vf->trusted) {
> -			dev_info(&pf->pdev->dev,
> -				 "VF %u no longer Trusted, deleting all cloud filters\n",
> -				 vf_id);
> -			i40e_del_all_cloud_filters(vf);
> -		}
> -	}
> -
>  out:
>  	clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
>  	return ret;

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 3/4] iavf: send MAC change request synchronously
  2026-08-21 20:45 ` [PATCH net 3/4] iavf: send MAC change request synchronously Tony Nguyen
@ 2026-08-24 19:01   ` Jakub Kicinski
  0 siblings, 0 replies; 9+ messages in thread
From: Jakub Kicinski @ 2026-08-24 19:01 UTC (permalink / raw)
  To: anthony.l.nguyen
  Cc: Jakub Kicinski, davem, pabeni, edumazet, andrew+netdev, netdev,
	jtornosm, przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov,
	sdf, horms, stable, rafal.romanowski

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
iavf: send MAC change request synchronously

This converts iavf_set_mac() from queueing the ADD_ETH_ADDR request for the
watchdog and waiting on vc_waitqueue into sending the virtchnl message
directly and polling the admin receive queue for the reply. A new generic
helper iavf_poll_virtchnl_response() is added, iavf_add_ether_addrs() now
returns an error code, iavf_mac_add_reject() is exported for rollback, and
vc_waitqueue is removed.

A few questions about the new synchronous path below.

First, about the changelog text:

>     export iavf_mac_add_reject() to enable proper rollback on local failures
>     (timeouts, send errors) - PF rejections are already handled automatically
>     by iavf_virtchnl_completion().

[Severity: Low]

Is the "timeouts" part accurate? iavf_poll_virtchnl_response() returns
-EAGAIN on timeout, and iavf_set_mac() explicitly skips the rollback for
that value:

	if (ret != -EAGAIN) {
		iavf_mac_add_reject(adapter);
		ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
	}

So on a timeout nothing is rolled back, which is the opposite of what the
changelog describes (and matches the in-code comment). Could the changelog
be adjusted to say send errors only?

> diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
> index dc31202b2a94..8c45536fd502 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf.h
> +++ b/drivers/net/ethernet/intel/iavf/iavf.h

[ ... ]

> @@ -588,8 +587,9 @@ void iavf_configure_queues(struct iavf_adapter *adapter);
>  void iavf_enable_queues(struct iavf_adapter *adapter);
>  void iavf_disable_queues(struct iavf_adapter *adapter);
>  void iavf_map_queues(struct iavf_adapter *adapter);
> -void iavf_add_ether_addrs(struct iavf_adapter *adapter);
> +int iavf_add_ether_addrs(struct iavf_adapter *adapter);
>  void iavf_del_ether_addrs(struct iavf_adapter *adapter);
> +void iavf_mac_add_reject(struct iavf_adapter *adapter);

[Severity: Low]

Now that iavf_add_ether_addrs() reports -EBUSY, -ENOMEM and send failures,
should the watchdog side caller consume it too? iavf_process_aq_command()
still discards it and returns 0 unconditionally:

	if (adapter->aq_required & IAVF_FLAG_AQ_ADD_MAC_FILTER) {
		iavf_add_ether_addrs(adapter);
		return 0;
	}

which also disagrees with that function's own kernel-doc ("Returns error
code if no command was sent or error code if the command failed").

[ ... ]

> diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c
> index 630388e9d28c..3fa288e3798a 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf_main.c
> +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
> @@ -1029,6 +1029,60 @@ static bool iavf_is_mac_set_handled(struct net_device *netdev,
>  	return ret;
>  }
>  
> +/**
> + * iavf_mac_change_done - Check if MAC change completed
> + * @adapter: board private structure
> + * @data: MAC address being checked (as const void *)
> + * @v_op: virtchnl opcode from processed message
> + *
> + * Callback for iavf_poll_virtchnl_response() to check if MAC change completed.
> + *
> + * Return: true if MAC change completed, false otherwise
> + */
> +static bool iavf_mac_change_done(struct iavf_adapter *adapter,
> +				 const void *data, enum virtchnl_ops v_op)
> +{
> +	const u8 *addr = data;
> +
> +	return iavf_is_mac_set_handled(adapter->netdev, addr);
> +}

[Severity: Medium]

iavf_mac_change_done() ignores v_op and looks only at filter state, while
iavf_poll_virtchnl_response() calls the predicate after every message it
processes.

Consider setting an address that already has a filter whose earlier add
succeeded, so add_handled is true and is_new_mac is false.
iavf_replace_primary_mac() sets add = true but does not reset add_handled,
and iavf_add_ether_addrs() clears add again while building the batch:

iavf_is_mac_set_handled():
	f = iavf_find_filter(adapter, macaddr);

	if (!f || (!f->add && f->add_handled))
		ret = true;

Can an unrelated message processed by the poll loop, for example a
VIRTCHNL_OP_EVENT link change, then make this return true before the
ADD_ETH_ADDR reply arrives? netdev->dev_addr is only updated from
iavf_virtchnl_completion(), so iavf_set_mac() would return -EACCES for a
request that is still in flight and later applied.

The old code only re-evaluated the condition on
wake_up(&adapter->vc_waitqueue), which was issued exclusively from the
VIRTCHNL_OP_ADD_ETH_ADDR completion paths.

> +
> +/**
> + * iavf_set_mac_sync - Synchronously change MAC address
> + * @adapter: board private structure
> + * @addr: MAC address to set
> + *
> + * Send MAC change request to PF and poll admin queue for response.
> + * Caller must hold netdev_lock. This can sleep for up to 2.5 seconds.
> + * Event buffer is allocated before sending to avoid state mismatch if
> + * allocation fails after message is sent to PF.
> + *
> + * Return: 0 on success, negative on failure
> + */
> +static int iavf_set_mac_sync(struct iavf_adapter *adapter, const u8 *addr)
> +{
> +	struct iavf_arq_event_info event;
> +	int ret;
> +
> +	netdev_assert_locked(adapter->netdev);
> +
> +	event.buf_len = IAVF_MAX_AQ_BUF_SIZE;
> +	event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL);
> +	if (!event.msg_buf)
> +		return -ENOMEM;
> +
> +	ret = iavf_add_ether_addrs(adapter);
> +	if (ret)
> +		goto out;
> +
> +	ret = iavf_poll_virtchnl_response(adapter, &event,
> +					  iavf_mac_change_done, addr, 2500);

[Severity: Medium]

Only one batch is sent here. iavf_add_ether_addrs() truncates the request
when it does not fit the admin queue buffer:

	len = virtchnl_struct_size(veal, list, count);
	if (len > IAVF_MAX_AQ_BUF_SIZE) {
		dev_warn(&adapter->pdev->dev, "Too many add MAC changes in one request\n");
		while (len > IAVF_MAX_AQ_BUF_SIZE)
			len = virtchnl_struct_size(veal, list, --count);
		more = true;
	}

The remaining filters keep f->add == true and IAVF_FLAG_AQ_ADD_MAC_FILTER
stays set, and iavf_add_filter() appends new filters to the tail of
mac_filter_list. If the just requested primary address ends up outside the
first batch, can iavf_mac_change_done() ever become true, given
iavf_is_mac_set_handled() requires !f->add? The poll loop has no way to send
the next batch, so this would burn the full 2500 ms and return -EAGAIN to
userspace while the watchdog applies the change afterwards.

> +
> +out:
> +	kfree(event.msg_buf);
> +	return ret;
> +}
> +
>  /**
>   * iavf_set_mac - NDO callback to set port MAC address
>   * @netdev: network interface device structure
> @@ -1049,25 +1103,23 @@ static int iavf_set_mac(struct net_device *netdev, void *p)
>  		return -EADDRNOTAVAIL;
>  
>  	ret = iavf_replace_primary_mac(adapter, addr->sa_data);
> -
>  	if (ret)
>  		return ret;
>  
> -	ret = wait_event_interruptible_timeout(adapter->vc_waitqueue,
> -					       iavf_is_mac_set_handled(netdev, addr->sa_data),
> -					       msecs_to_jiffies(2500));
> -
> -	/* If ret < 0 then it means wait was interrupted.
> -	 * If ret == 0 then it means we got a timeout.
> -	 * else it means we got response for set MAC from PF,
> -	 * check if netdev MAC was updated to requested MAC,
> -	 * if yes then set MAC succeeded otherwise it failed return -EACCES
> -	 */
> -	if (ret < 0)
> +	ret = iavf_set_mac_sync(adapter, addr->sa_data);
> +	if (ret) {
> +		/* Rollback only if send failed (message never reached PF).
> +		 * Don't rollback on timeout (-EAGAIN) because the message was
> +		 * sent and PF will eventually respond. When the response arrives,
> +		 * iavf_virtchnl_completion() will handle rollback (on PF error)
> +		 * or acceptance (on PF success) automatically.
> +		 */
> +		if (ret != -EAGAIN) {
> +			iavf_mac_add_reject(adapter);
> +			ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr);
> +		}

[Severity: High]

Can this rollback destroy filters that belong to a different, still
outstanding request?

iavf_mac_add_reject() is a completion time helper that walks the whole list:

	list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
		if (f->remove && ether_addr_equal(f->macaddr, netdev->dev_addr))
			f->remove = false;

		if (!f->add && !f->add_handled)
			f->add_handled = true;

		if (f->is_new_mac) {
			list_del(&f->list);
			kfree(f);
		}
	}

One way to reach it with ret == -EBUSY is an ADD_ETH_ADDR batch the watchdog
already sent and whose reply has not been processed yet. Those filters are
exactly in state add == false, is_new_mac == true (is_new_mac is only
cleared by iavf_mac_add_ok() on a successful reply), so they are freed here
while the PF still has the adds pending.

Newly queued unicast/multicast filters from iavf_addr_sync() are in the same
boat, since iavf_add_filter() sets:

	f->add = true;
	f->add_handled = false;
	f->is_new_mac = true;

The netdev core already considers those addresses synced, so once they are
dropped here nothing programs or retries them until a reset. Should the
rollback be scoped to the address that iavf_set_mac() itself queued?

[Severity: Medium]

Does the -EAGAIN assumption in the comment above ("the message was sent and
PF will eventually respond") hold when IAVF_FLAG_PF_COMMS_FAILED is set?
iavf_send_pf_msg() returns success without posting anything in that case:

	if (adapter->flags & IAVF_FLAG_PF_COMMS_FAILED)
		return 0; /* nothing to see here, move along */

iavf_add_ether_addrs() then reports 0 while current_op stays
VIRTCHNL_OP_ADD_ETH_ADDR and f->add has already been cleared, so the poll
spins for the full 2.5 s under rtnl_lock plus the netdev instance lock,
returns -EAGAIN, and no rollback runs. adapter->hw.mac.addr and the filter
bookkeeping then diverge from netdev->dev_addr, and current_op stays pending
so later virtchnl requests hit "Cannot add filters, command %d pending".

iavf_disable_vf() sets IAVF_FLAG_PF_COMMS_FAILED, clears
IAVF_FLAG_RESET_PENDING and moves the state to __IAVF_DOWN, so the guard at
the top of iavf_set_mac() still passes in that window:

	if (iavf_is_reset_in_progress(adapter) || adapter->state < __IAVF_DOWN)
		return -EBUSY;

>  		return ret;
> -
> -	if (!ret)
> -		return -EAGAIN;
> +	}
>  
>  	if (!ether_addr_equal(netdev->dev_addr, addr->sa_data))
>  		return -EACCES;

[ ... ]

> diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
> index ec234cc8bd9d..e6b7e8f82c7c 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c

[ ... ]

> @@ -555,20 +556,23 @@ iavf_set_mac_addr_type(struct virtchnl_ether_addr *virtchnl_ether_addr,
>   * @adapter: adapter structure
>   *
>   * Request that the PF add one or more addresses to our filters.
> - **/
> -void iavf_add_ether_addrs(struct iavf_adapter *adapter)
> + *
> + * Return: 0 on success, negative on failure
> + */
> +int iavf_add_ether_addrs(struct iavf_adapter *adapter)
>  {
>  	struct virtchnl_ether_addr_list *veal;
>  	struct iavf_mac_filter *f;
>  	int i = 0, count = 0;
>  	bool more = false;
>  	size_t len;
> +	int ret;
>  
>  	if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
>  		/* bail because we already have a command pending */
>  		dev_err(&adapter->pdev->dev, "Cannot add filters, command %d pending\n",
>  			adapter->current_op);
> -		return;
> +		return -EBUSY;
>  	}

[Severity: Medium]

Is this -EBUSY reachable as a hard failure from the new caller?

iavf_set_mac() runs with the netdev instance lock held, and the only context
that clears current_op is iavf_virtchnl_completion() as called from
iavf_adminq_task(), which starts with netdev_lock(netdev). The watchdog does
the same. So while iavf_set_mac() holds the lock, an outstanding command's
reply cannot be consumed and current_op cannot go back to
VIRTCHNL_OP_UNKNOWN.

iavf_set_mac_sync() sends once and gives up:

	ret = iavf_add_ether_addrs(adapter);
	if (ret)
		goto out;

Before this change iavf_set_mac() only set IAVF_FLAG_AQ_ADD_MAC_FILTER and
the watchdog retried the send after the previous command completed, so the
operation succeeded. Can "ip link set dev X address ..." or bond_enslave()
now fail with -EBUSY whenever the watchdog has a command in flight, for
instance the VIRTCHNL_OP_GET_STATS request it sends every couple of
watchdog ticks?

[ ... ]

> @@ -615,8 +620,15 @@ void iavf_add_ether_addrs(struct iavf_adapter *adapter)
>  
>  	spin_unlock_bh(&adapter->mac_vlan_list_lock);
>  
> -	iavf_send_pf_msg(adapter, VIRTCHNL_OP_ADD_ETH_ADDR, (u8 *)veal, len);
> +	ret = iavf_send_pf_msg(adapter, VIRTCHNL_OP_ADD_ETH_ADDR, (u8 *)veal, len);
>  	kfree(veal);
> +	if (ret) {
> +		dev_err(&adapter->pdev->dev,
> +			"Unable to send ADD_ETH_ADDR message to PF, error %d\n", ret);
> +		adapter->current_op = VIRTCHNL_OP_UNKNOWN;
> +	}
> +
> +	return ret;
>  }
>  
>  /**
> @@ -712,8 +724,8 @@ static void iavf_mac_add_ok(struct iavf_adapter *adapter)
>   * @adapter: adapter structure
>   *
>   * Remove filters from list based on PF response.
> - **/
> -static void iavf_mac_add_reject(struct iavf_adapter *adapter)
> + */
> +void iavf_mac_add_reject(struct iavf_adapter *adapter)
>  {
>  	struct net_device *netdev = adapter->netdev;
>  	struct iavf_mac_filter *f, *ftmp;

[Severity: Medium]

Is this helper able to roll back a filter whose is_new_mac is already
cleared?

Setting an address that was programmed successfully before leaves
is_new_mac == false, and iavf_replace_primary_mac() then does:

	old_f->is_primary = false;
	old_f->remove = true;
	...
	new_f->is_primary = true;
	new_f->add = true;

If iavf_set_mac_sync() fails locally before the batch is built (-EBUSY from
the current_op check, or -ENOMEM from the veal kzalloc), both branches in
iavf_mac_add_reject() skip that entry: f->add is still true, and
f->is_new_mac is false. So f->add and IAVF_FLAG_AQ_ADD_MAC_FILTER survive,
and the watchdog later sends ADD_ETH_ADDR for the address whose change was
just reported as failed. Since f->is_primary is still set,
iavf_set_mac_addr_type() marks it as primary:

	virtchnl_ether_addr->type = filter->is_primary ?
		VIRTCHNL_ETHER_ADDR_PRIMARY :
		VIRTCHNL_ETHER_ADDR_EXTRA;

Does the PF then end up with a primary MAC that differs from
netdev->dev_addr, with no filter marked primary for the address the
interface is actually using, since old_f->is_primary is never restored?

[ ... ]

> @@ -2950,3 +2960,72 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
>  	} /* switch v_opcode */
>  	adapter->current_op = VIRTCHNL_OP_UNKNOWN;
>  }
> +
> +/**
> + * iavf_poll_virtchnl_response - Poll admin queue for virtchnl response

[ ... ]

> + * Return: 0 on success (condition met), -EAGAIN on timeout, or error code
> + */
> +int iavf_poll_virtchnl_response(struct iavf_adapter *adapter,
> +				struct iavf_arq_event_info *event,
> +				bool (*condition)(struct iavf_adapter *adapter,
> +						  const void *data,
> +						  enum virtchnl_ops v_op),
> +				const void *cond_data,
> +				unsigned int timeout_ms)
> +{
> +	struct iavf_hw *hw = &adapter->hw;
> +	enum virtchnl_ops received_op;
> +	unsigned long timeout;
> +	int ret = -EAGAIN;
> +	u16 pending = 0;
> +	u32 v_retval;
> +
> +	netdev_assert_locked(adapter->netdev);
> +
> +	timeout = jiffies + msecs_to_jiffies(timeout_ms);
> +	do {
> +		if (!pending)
> +			usleep_range(50, 75);
> +
> +		if (iavf_clean_arq_element(hw, event, &pending) == IAVF_SUCCESS) {

[Severity: Medium]

The kernel-doc promises "or error code", but ret is only ever -EAGAIN or 0,
and the iavf_clean_arq_element() status is dropped here. The sibling helper
in this file does convert it:

iavf_poll_virtchnl_msg():
	status = iavf_clean_arq_element(hw, event, NULL);
	if (status != IAVF_SUCCESS)
		return iavf_status_to_errno(status);

For the IAVF_ERR_ADMIN_QUEUE_ERROR case, iavf_clean_arq_element() has
already consumed the descriptor, re-posted it and advanced IAVF_VF_ARQT1:

	flags = le16_to_cpu(desc->flags);
	if (flags & LIBIE_AQ_FLAG_ERR) {
		ret_code = IAVF_ERR_ADMIN_QUEUE_ERROR;

so the awaited reply no longer exists. Should that be reported instead of
spinning to the -EAGAIN timeout, which iavf_set_mac() reads as "still
outstanding, the PF will respond later" and therefore skips the rollback,
leaving current_op pending?

[Severity: High]

What happens to this loop if a VFR/EMPR lands while it is polling? The other
ARQ consumer checks for that explicitly:

iavf_adminq_task():
	if (iavf_is_reset_in_progress(adapter))
		goto freedom;

	/* check for error indications */
	val = rd32(hw, IAVF_VF_ARQLEN1);
	if (val == 0xdeadbeef || val == 0xffffffff) /* device in reset */
		goto freedom;

and it also clears the sticky ARQVFE/ARQOVFL/ARQCRIT bits. None of that
happens here, and iavf_clean_arq_element() takes the producer index straight
from the register without validating it:

	ntu = rd32(hw, IAVF_VF_ARQH1) & IAVF_VF_ARQH1_ARQH_MASK;
	if (ntu == ntc) {

With the reset sentinels, 0xdeadbeef & 0x3FF is 751 and 0xffffffff & 0x3FF
is 1023, both well above hw->aq.num_arq_entries and never equal to
next_to_clean, so IAVF_ERR_ADMIN_QUEUE_NO_WORK is not returned. Can this
then "clean" descriptors the hardware never produced, rewrite them, do
wr32(hw, IAVF_VF_ARQT1, ntc) on a disabled queue and advance
next_to_clean/next_to_use?

*pending would also come back as hundreds, so the throttle is skipped:

	if (!pending)
		usleep_range(50, 75);
	...
		if (pending)
			continue;

Does that busy-spin on MMIO for the remaining 2.5 s while holding the netdev
instance lock and the caller's rtnl_lock, which is what iavf_reset_task()
needs to recover? Note the loop itself can process
VIRTCHNL_EVENT_RESET_IMPENDING, which only schedules the reset and does not
stop the polling.

> +			received_op = (enum virtchnl_ops)le32_to_cpu(event->desc.cookie_high);
> +			if (received_op != VIRTCHNL_OP_UNKNOWN) {
> +				v_retval = le32_to_cpu(event->desc.cookie_low);
> +
> +				iavf_virtchnl_completion(adapter, received_op,
> +							 (enum iavf_status)v_retval,
> +							 event->msg_buf, event->msg_len);
> +
> +				if (condition(adapter, cond_data, received_op)) {
> +					ret = 0;
> +					break;
> +				}
> +			}
> +
> +			memset(event->msg_buf, 0, IAVF_MAX_AQ_BUF_SIZE);

[Severity: Low]

Should this use event->buf_len rather than the hardcoded
IAVF_MAX_AQ_BUF_SIZE? The buffer is caller owned and its size is described
by event->buf_len, which iavf_clean_arq_element() honours:

	e->msg_len = min(datalen, e->buf_len);

Today's only caller allocates exactly IAVF_MAX_AQ_BUF_SIZE so nothing is
overwritten, but the helper is exported in iavf.h as generic, and this file
already has ARQ polling callers with small buffers, e.g.:

	event.msg_buf = (u8 *)&rxdids;
	event.buf_len = sizeof(rxdids);

Such a caller would get a 4096 byte memset of a few bytes of storage.

> +
> +			if (pending)
> +				continue;
> +		}
> +	} while (time_before(jiffies, timeout));
> +
> +	return ret;
> +}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 4/4] ice: skip unnecessary VF reset when setting trust
  2026-08-21 20:45 ` [PATCH net 4/4] ice: skip unnecessary VF reset when setting trust Tony Nguyen
@ 2026-08-24 19:01   ` Jakub Kicinski
  0 siblings, 0 replies; 9+ messages in thread
From: Jakub Kicinski @ 2026-08-24 19:01 UTC (permalink / raw)
  To: anthony.l.nguyen
  Cc: Jakub Kicinski, davem, pabeni, edumazet, andrew+netdev, netdev,
	jtornosm, przemyslaw.kitszel, jacob.e.keller, aleksandr.loktionov,
	sdf, horms, stable, rafal.romanowski

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
    ice: skip unnecessary VF reset when setting trust

The patch makes ice_set_vf_trust() skip the VF reset in most cases: granting
trust only sets the ICE_VIRTCHNL_VF_CAP_PRIVILEGE bit, and revoking trust
only resets when vf->num_mac_lldp is non-zero or one of the promiscuous
state bits is set.  A new helper, ice_setup_vf_trust(), toggles the
capability bit for the no-reset path.

A few questions about the new revocation logic below.

> diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c
> index e04de0215596..558937899818 100644
> --- a/drivers/net/ethernet/intel/ice/ice_sriov.c
> +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c
> @@ -1366,6 +1366,23 @@ int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
>  	return __ice_set_vf_mac(ice_netdev_to_pf(netdev), vf_id, mac);
>  }
>  
> +/**
> + * ice_setup_vf_trust - Enable/disable VF trust mode without reset
> + * @vf: VF to configure
> + * @setting: trust setting
> + *
> + * Update VF flags when changing trust without performing a VF reset.
> + * This is only called when it's safe to skip the reset (VF has no advanced
> + * features configured that need cleanup).
> + */
> +static void ice_setup_vf_trust(struct ice_vf *vf, bool setting)
> +{
> +	if (setting)
> +		set_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
> +	else
> +		clear_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
> +}

[Severity: Low]
This isn't a bug, but doesn't ice_vf_lib.c already have a helper with
exactly these semantics?

drivers/net/ethernet/intel/ice/ice_vf_lib.c:
static void ice_vf_set_host_trust_cfg(struct ice_vf *vf)
{
	assign_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps, vf->trusted);
}

It is called from ice_vf_rebuild_host_cfg() on the reset path.  Would it be
preferable to export that one (ice_vf_lib_private.h already declares
ice_is_vf_trusted()) rather than carry a second copy of the rule that
derives ICE_VIRTCHNL_VF_CAP_PRIVILEGE from vf->trusted in a different file?

>  /**
>   * ice_set_vf_trust
>   * @netdev: network interface device structure
> @@ -1401,11 +1418,19 @@ int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted)
>  
>  	mutex_lock(&vf->cfg_lock);
>  
> -	while (!trusted && vf->num_mac_lldp)
> -		ice_vf_update_mac_lldp_num(vf, ice_get_vf_vsi(vf), false);
> -
> -	vf->trusted = trusted;
> -	ice_reset_vf(vf, ICE_VF_RESET_NOTIFY);
> +	/* Reset only if revoking trust and VF has advanced features configured */
> +	if (!trusted &&
> +	    (vf->num_mac_lldp > 0 ||
> +	     test_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states) ||
> +	     test_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states))) {

[Severity: High]
Is this predicate complete with respect to everything trust gates in ice?
Besides LLDP filters and promiscuous mode, vf->trusted also gates the MAC
and VLAN filter quotas and the administratively assigned MAC:

drivers/net/ethernet/intel/ice/virt/virtchnl.c:ice_can_vf_change_mac() {
	if (vf->pf_set_mac && !ice_is_vf_trusted(vf))
		return false;
}

drivers/net/ethernet/intel/ice/virt/virtchnl.c:ice_vc_handle_mac_addr_msg() {
	if (set && !ice_is_vf_trusted(vf) &&
	    (vf->num_mac + al->num_elements) > ICE_MAX_MACADDR_PER_VF) {
}

drivers/net/ethernet/intel/ice/virt/virtchnl.c:ice_vf_has_max_vlans() {
	if (ice_is_vf_trusted(vf))
		return false;
}

So a guest that was trusted can have installed extra unicast/multicast MAC
filters, a MAC that overrides the pf_set_mac address, and more than
ICE_MAX_VLAN_PER_VF VLANs.  None of that shows up in num_mac_lldp or the
promisc bits, so with those clear, "ip link set <pf> vf N trust off" takes
the new else branch.

Do those hardware filters then stay programmed after the log prints "VF N is
now untrusted"?  Previously the unconditional reset reached:

drivers/net/ethernet/intel/ice/ice_vf_lib.c:ice_vf_reconfig_vsi() {
	ice_vsi_decfg(vsi);
	ice_fltr_remove_all(vsi);
}

followed by ice_vf_rebuild_host_cfg(), which re-adds only broadcast plus the
host-sanctioned MAC/VLAN config.

The software counters look affected too.  They are cleared only on the reset
path:

drivers/net/ethernet/intel/ice/ice_vf_lib.c:ice_vf_clear_counters() {
	if (vsi)
		vsi->num_vlan = 0;

	vf->num_mac = 0;
	vf->num_mac_lldp = 0;
}

Can vf->num_mac therefore remain above ICE_MAX_MACADDR_PER_VF after trust is
revoked, so that later legitimate MAC adds from the now-untrusted VF are
rejected until some unrelated reset happens?

Relatedly, the kernel-doc on ice_setup_vf_trust() says it "is only called
when it's safe to skip the reset (VF has no advanced features configured
that need cleanup)", and the commit message says the features needing cleanup
are "(MAC LLDP filters, promiscuous mode)".  Should both mention the MAC and
VLAN filter state as well?

> +		while (vf->num_mac_lldp)
> +			ice_vf_update_mac_lldp_num(vf, ice_get_vf_vsi(vf), false);
> +		vf->trusted = trusted;
> +		ice_reset_vf(vf, ICE_VF_RESET_NOTIFY);
> +	} else {
> +		vf->trusted = trusted;
> +		ice_setup_vf_trust(vf, trusted);
> +	}

[Severity: High]
What happens to the negotiated VLAN V2 capabilities when trust changes
without a reset?  The advertised limit is derived from vf->trusted once, at
negotiation time, and then cached on the PF:

drivers/net/ethernet/intel/ice/virt/virtchnl.c:ice_vc_get_max_vlan_fltrs() {
	if (vf->trusted)
		return VLAN_N_VID;
	else
		return ICE_MAX_VLAN_PER_VF;
}

drivers/net/ethernet/intel/ice/virt/virtchnl.c:ice_vc_get_offload_vlan_v2_caps() {
	/* store negotiated caps to prevent invalid VF messages */
	memcpy(&vf->vlan_v2_caps, caps, sizeof(*caps));
}

VLAN V2 adds are validated against that snapshot only:

drivers/net/ethernet/intel/ice/virt/virtchnl.c:ice_vc_validate_add_vlan_filter_list() {
	if (num_requested_filters > vfc->max_filters)
		return false;
}

and the snapshot is invalidated only on the reset path:

drivers/net/ethernet/intel/ice/ice_vf_lib.c:ice_vf_set_initialized() {
	memset(&vf->vlan_v2_caps, 0, sizeof(vf->vlan_v2_caps));
}

If the VF negotiated VIRTCHNL_VF_OFFLOAD_VLAN_V2 while trusted, does the
else branch leave max_filters at VLAN_N_VID, allowing the now-untrusted VF
to keep programming VLAN filters well beyond ICE_MAX_VLAN_PER_VF?

In the other direction, after granting trust without a reset both the PF
cache and the guest cache still hold 8:

drivers/net/ethernet/intel/iavf/iavf_main.c:iavf_get_max_vlans_allowed() {
	else if (VLAN_V2_ALLOWED(adapter))
		return adapter->vlan_v2_caps.filtering.max_filters;
}

so is the commit message statement "When granting trust, no reset is needed
- we can just set the capability flag to allow privileged operations"
accurate for VLAN filtering, where the privilege only becomes usable after
renegotiation?

[Severity: Medium]
This isn't a bug introduced by this patch, but while ice_set_vf_trust() is
being touched: the switchdev check earlier in this same function returns
without releasing the VF reference taken by ice_get_vf_by_id():

	if (ice_is_eswitch_mode_switchdev(pf)) {
		dev_info(ice_pf_to_dev(pf), "Trusted VF is forbidden in switchdev mode\n");
		return -EOPNOTSUPP;
	}

Every other exit uses "goto out_put_vf;" so that ice_put_vf() drops the
kref_get_unless_zero() reference.  Since the leaked reference keeps the
refcount above zero, ice_release_vf() never runs, so neither
pci_dev_put(vf->vfdev) nor the kfree_rcu() of the ice_vf ever happens when
SR-IOV is disabled or the driver is unbound.  Would it make sense to convert
that return into "goto out_put_vf;" here?

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-08-24 19:01 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21 20:45 [PATCH net 0/4][pull request] Fix i40e/ice/iavf VF bonding after netdev lock changes Tony Nguyen
2026-08-21 20:45 ` [PATCH net 1/4] iavf: return EBUSY if reset in progress or not ready during MAC change Tony Nguyen
2026-08-24 19:01   ` Jakub Kicinski
2026-08-21 20:45 ` [PATCH net 2/4] i40e: skip unnecessary VF reset when setting trust Tony Nguyen
2026-08-24 19:01   ` Jakub Kicinski
2026-08-21 20:45 ` [PATCH net 3/4] iavf: send MAC change request synchronously Tony Nguyen
2026-08-24 19:01   ` Jakub Kicinski
2026-08-21 20:45 ` [PATCH net 4/4] ice: skip unnecessary VF reset when setting trust Tony Nguyen
2026-08-24 19:01   ` Jakub Kicinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox