[PATCH net v3 5/5] iavf: refactor virtchnl polling into single function

Jose Ignacio Tornos Martinez
2026-04-14T11:00:50Z

At this moment, the driver has two separate functions for polling virtchnl
messages from the admin queue:
- iavf_poll_virtchnl_msg() for init-time (no timeout, no completion
handler)
- iavf_poll_virtchnl_response() for runtime (with timeout, calls
completion)

Refactor by enhancing iavf_poll_virtchnl_msg() to handle both use cases:
1. Init-time mode (timeout_ms=0):
- Polls until matching opcode found or queue empty
- Returns raw message data without processing through completion handler
- Exits immediately on empty queue (no sleep/retry)
2. Runtime mode (timeout_ms>0):
- Polls with timeout using condition callback or opcode check
- Processes all messages through iavf_virtchnl_completion()
- Supports custom completion callback (takes priority) or falls back
to checking adapter->current_op against expected opcode
- Uses pending parameter to skip sleep when more messages queued
- Uses 50-75 usec sleep (due to commit 9e3f23f44f32 ("i40e: reduce wait
time for adminq command completion"))

By unifying message handling, both init-time and runtime messages can be
processed through the completion handler when appropriate, ensuring
consistent state updates and maintaining backward compatibility with all
existing call sites.

Suggested-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
drivers/net/ethernet/intel/iavf/iavf.h | 9 +-
drivers/net/ethernet/intel/iavf/iavf_main.c | 13 +-
.../net/ethernet/intel/iavf/iavf_virtchnl.c | 247 ++++++++----------
3 files changed, 125 insertions(+), 144 deletions(-)

diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
index b012a91b0252..9b25c5a65d2a 100644
--- a/drivers/net/ethernet/intel/iavf/iavf.h
+++ b/drivers/net/ethernet/intel/iavf/iavf.h
@@ -607,11 +607,10 @@ 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,
- bool (*condition)(struct iavf_adapter *, const void *),
- const void *cond_data,
- enum virtchnl_ops v_opcode,
- unsigned int timeout_ms);
+int iavf_poll_virtchnl_msg(struct iavf_hw *hw, struct iavf_arq_event_info *event,
+ enum virtchnl_ops op_to_poll, unsigned int timeout_ms,
+ bool (*condition)(struct iavf_adapter *, const void *),
+ const void *cond_data);
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 80277d495a8d..b0db15fd8ddb 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_main.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
@@ -1075,6 +1075,7 @@ static bool iavf_mac_change_done(struct iavf_adapter *adapter, const void *data)
*/
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);
@@ -1083,8 +1084,16 @@ static int iavf_set_mac_sync(struct iavf_adapter *adapter, const u8 *addr)
if (ret)
return ret;

- return iavf_poll_virtchnl_response(adapter, iavf_mac_change_done, addr,
- VIRTCHNL_OP_UNKNOWN, 2500);
+ 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_poll_virtchnl_msg(&adapter->hw, &event, VIRTCHNL_OP_UNKNOWN,
+ 2500, iavf_mac_change_done, addr);
+
+ kfree(event.msg_buf);
+ return ret;
}

/**
diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
index df124f840ddb..ef9a251060d9 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
@@ -54,55 +54,121 @@ int iavf_send_api_ver(struct iavf_adapter *adapter)
}

/**
- * iavf_poll_virtchnl_msg
+ * iavf_virtchnl_completion_done - Check if virtchnl operation completed
+ * @adapter: adapter structure
+ * @condition: optional callback for custom completion check
+ * @cond_data: context data for callback
+ * @op_to_poll: opcode to check against current_op (if no callback)
+ *
+ * Checks if operation is complete. Callback takes priority if provided,
+ * otherwise checks if current_op matches op_to_poll.
+ *
+ * Return: true if operation completed
+ */
+static inline bool
+iavf_virtchnl_completion_done(struct iavf_adapter *adapter,
+ bool (*condition)(struct iavf_adapter *, const void *),
+ const void *cond_data,
+ enum virtchnl_ops op_to_poll)
+{
+ if (condition)
+ return condition(adapter, cond_data);
+
+ return adapter->current_op == op_to_poll;
+}
+
+/**
+ * iavf_poll_virtchnl_msg - Poll admin queue for virtchnl message
* @hw: HW configuration structure
* @event: event to populate on success
- * @op_to_poll: requested virtchnl op to poll for
+ * @op_to_poll: virtchnl opcode to poll for (used for init-time and runtime
+ * without callback)
+ * @timeout_ms: timeout in milliseconds (0 = no timeout, exit on empty queue)
+ * @condition: optional callback to check custom completion (runtime use,
+ * takes priority over op_to_poll check)
+ * @cond_data: context data for condition callback
+ *
+ * Enhanced polling function that handles both init-time and runtime use cases:
+ * - Init-time: Set op_to_poll, timeout_ms=0, condition=NULL
+ * Polls until matching opcode found or queue empty
+ * - Runtime with callback: Set timeout_ms>0, condition callback, cond_data
+ * Polls with timeout until condition returns true (op_to_poll not used)
+ * - Runtime without callback: Set op_to_poll, timeout_ms>0, condition=NULL
+ * Polls with timeout until adapter->current_op == op_to_poll
+ *
+ * Runtime messages are processed through iavf_virtchnl_completion().
+ * For init-time use, returns 0 with raw message data in event buffer.
+ * For runtime use, returns 0 when completion condition is met.
*
- * Initialize poll for virtchnl msg matching the requested_op. Returns 0
- * if a message of the correct opcode is in the queue or an error code
- * if no message matching the op code is waiting and other failures.
+ * Return: 0 on success, -EAGAIN on timeout, or error code
*/
-static int
-iavf_poll_virtchnl_msg(struct iavf_hw *hw, struct iavf_arq_event_info *event,
- enum virtchnl_ops op_to_poll)
+int iavf_poll_virtchnl_msg(struct iavf_hw *hw, struct iavf_arq_event_info *event,
+ enum virtchnl_ops op_to_poll, unsigned int timeout_ms,
+ bool (*condition)(struct iavf_adapter *, const void *),
+ const void *cond_data)
{
+ struct iavf_adapter *adapter = hw->back;
+ unsigned long timeout = timeout_ms ? jiffies + msecs_to_jiffies(timeout_ms) : 0;
enum virtchnl_ops received_op;
enum iavf_status status;
- u32 v_retval;
+ u32 v_retval = 0;
+ u16 pending;

- while (1) {
- /* When the AQ is empty, iavf_clean_arq_element will return
- * nonzero and this loop will terminate.
- */
- status = iavf_clean_arq_element(hw, event, NULL);
- if (status != IAVF_SUCCESS)
- return iavf_status_to_errno(status);
- received_op =
- (enum virtchnl_ops)le32_to_cpu(event->desc.cookie_high);
+ do {
+ if (timeout_ms && iavf_virtchnl_completion_done(adapter, condition,
+ cond_data, op_to_poll))
+ return 0;

- if (received_op == VIRTCHNL_OP_EVENT) {
- struct iavf_adapter *adapter = hw->back;
- struct virtchnl_pf_event *vpe =
- (struct virtchnl_pf_event *)event->msg_buf;
+ status = iavf_clean_arq_element(hw, event, &pending);
+ if (status == IAVF_SUCCESS) {
+ received_op = (enum virtchnl_ops)le32_to_cpu(event->desc.cookie_high);

- if (vpe->event != VIRTCHNL_EVENT_RESET_IMPENDING)
- continue;
+ /* Handle reset events specially */
+ if (received_op == VIRTCHNL_OP_EVENT) {
+ struct virtchnl_pf_event *vpe =
+ (struct virtchnl_pf_event *)event->msg_buf;

- dev_info(&adapter->pdev->dev, "Reset indication received from the PF\n");
- if (!(adapter->flags & IAVF_FLAG_RESET_PENDING))
- iavf_schedule_reset(adapter,
- IAVF_FLAG_RESET_PENDING);
+ if (vpe->event != VIRTCHNL_EVENT_RESET_IMPENDING)
+ continue;
+
+ dev_info(&adapter->pdev->dev,
+ "Reset indication received from the PF\n");
+ if (!(adapter->flags & IAVF_FLAG_RESET_PENDING))
+ iavf_schedule_reset(adapter,
+ IAVF_FLAG_RESET_PENDING);
+
+ return -EIO;
+ }
+
+ v_retval = le32_to_cpu(event->desc.cookie_low);
+
+ if (!timeout_ms) {
+ if (received_op == op_to_poll)
+ return virtchnl_status_to_errno((enum virtchnl_status_code)
+ v_retval);
+ } else {
+ iavf_virtchnl_completion(adapter, received_op,
+ (enum iavf_status)v_retval,
+ event->msg_buf, event->msg_len);
+ }
+
+ if (pending)
+ continue;
+ } else if (!timeout_ms) {
+ return iavf_status_to_errno(status);
+ }

- return -EIO;
+ if (timeout_ms) {
+ memset(event->msg_buf, 0, IAVF_MAX_AQ_BUF_SIZE);
+ usleep_range(50, 75);
}

- if (op_to_poll == received_op)
- break;
- }
+ } while (!timeout_ms || time_before(jiffies, timeout));
+
+ if (iavf_virtchnl_completion_done(adapter, condition, cond_data, op_to_poll))
+ return 0;

- v_retval = le32_to_cpu(event->desc.cookie_low);
- return virtchnl_status_to_errno((enum virtchnl_status_code)v_retval);
+ return -EAGAIN;
}

/**
@@ -124,7 +190,8 @@ int iavf_verify_api_ver(struct iavf_adapter *adapter)
if (!event.msg_buf)
return -ENOMEM;

- err = iavf_poll_virtchnl_msg(&adapter->hw, &event, VIRTCHNL_OP_VERSION);
+ err = iavf_poll_virtchnl_msg(&adapter->hw, &event, VIRTCHNL_OP_VERSION,
+ 0, NULL, NULL);
if (!err) {
struct virtchnl_version_info *pf_vvi =
(struct virtchnl_version_info *)event.msg_buf;
@@ -294,7 +361,8 @@ int iavf_get_vf_config(struct iavf_adapter *adapter)
if (!event.msg_buf)
return -ENOMEM;

- err = iavf_poll_virtchnl_msg(hw, &event, VIRTCHNL_OP_GET_VF_RESOURCES);
+ err = iavf_poll_virtchnl_msg(hw, &event, VIRTCHNL_OP_GET_VF_RESOURCES,
+ 0, NULL, NULL);
memcpy(adapter->vf_res, event.msg_buf, min(event.msg_len, len));

/* some PFs send more queues than we should have so validate that
@@ -322,7 +390,8 @@ int iavf_get_vf_vlan_v2_caps(struct iavf_adapter *adapter)
return -ENOMEM;

err = iavf_poll_virtchnl_msg(&adapter->hw, &event,
- VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS);
+ VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS,
+ 0, NULL, NULL);
if (!err)
memcpy(&adapter->vlan_v2_caps, event.msg_buf,
min(event.msg_len, len));
@@ -342,7 +411,8 @@ int iavf_get_vf_supported_rxdids(struct iavf_adapter *adapter)
event.buf_len = sizeof(rxdids);

err = iavf_poll_virtchnl_msg(&adapter->hw, &event,
- VIRTCHNL_OP_GET_SUPPORTED_RXDIDS);
+ VIRTCHNL_OP_GET_SUPPORTED_RXDIDS,
+ 0, NULL, NULL);
if (!err)
adapter->supp_rxdids = rxdids;

@@ -359,7 +429,8 @@ int iavf_get_vf_ptp_caps(struct iavf_adapter *adapter)
event.buf_len = sizeof(caps);

err = iavf_poll_virtchnl_msg(&adapter->hw, &event,
- VIRTCHNL_OP_1588_PTP_GET_CAPS);
+ VIRTCHNL_OP_1588_PTP_GET_CAPS,
+ 0, NULL, NULL);
if (!err)
adapter->ptp.hw_caps = caps;

@@ -2961,101 +3032,3 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
adapter->current_op = VIRTCHNL_OP_UNKNOWN;
}

-/**
- * iavf_virtchnl_done - Check if virtchnl operation completed
- * @adapter: board private structure
- * @condition: optional callback for custom completion check
- * (takes priority)
- * @cond_data: context data for callback
- * @v_opcode: virtchnl opcode value we're waiting for if no condition
- * configured (typically VIRTCHNL_OP_UNKNOWN), if condition not used
- *
- * Checks completion status. Callback takes priority if provided. Otherwise
- * waits for current_op to reach v_opcode (typically VIRTCHNL_OP_UNKNOWN
- * after completion).
- *
- * Return: true if operation completed
- */
-static inline bool iavf_virtchnl_done(struct iavf_adapter *adapter,
- bool (*condition)(struct iavf_adapter *, const void *),
- const void *cond_data,
- enum virtchnl_ops v_opcode)
-{
- if (condition)
- return condition(adapter, cond_data);
-
- return adapter->current_op == v_opcode;
-}
-
-/**
- * iavf_poll_virtchnl_response - Poll admin queue for virtchnl response
- * @adapter: board private structure
- * @condition: optional callback to check if desired response received
- * (takes priority)
- * @cond_data: context data passed to condition callback
- * @v_opcode: virtchnl opcode value to wait for if no condition configured
- * (typically VIRTCHNL_OP_UNKNOWN), if condition, not used
- * @timeout_ms: maximum time to wait in milliseconds
- *
- * Polls admin queue and processes all messages until condition returns true
- * or timeout expires. If condition is NULL, waits for current_op to become
- * v_opcode (typically VIRTCHNL_OP_UNKNOWN after operation completes).
- * 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
- */
-int iavf_poll_virtchnl_response(struct iavf_adapter *adapter,
- bool (*condition)(struct iavf_adapter *, const void *),
- const void *cond_data,
- enum virtchnl_ops v_opcode,
- unsigned int timeout_ms)
-{
- struct iavf_hw *hw = &adapter->hw;
- struct iavf_arq_event_info event;
- enum virtchnl_ops v_op;
- enum iavf_status v_ret;
- unsigned long timeout;
- u16 pending;
- 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;
-
- timeout = jiffies + msecs_to_jiffies(timeout_ms);
- do {
- if (iavf_virtchnl_done(adapter, condition, cond_data, v_opcode)) {
- ret = 0;
- goto out;
- }
-
- ret = iavf_clean_arq_element(hw, &event, &pending);
- if (!ret) {
- v_op = (enum virtchnl_ops)le32_to_cpu(event.desc.cookie_high);
- v_ret = (enum iavf_status)le32_to_cpu(event.desc.cookie_low);
-
- iavf_virtchnl_completion(adapter, v_op, v_ret,
- event.msg_buf, event.msg_len);
-
- memset(event.msg_buf, 0, IAVF_MAX_AQ_BUF_SIZE);
-
- if (pending)
- continue;
- }
-
- usleep_range(50, 75);
- } while (time_before(jiffies, timeout));
-
- if (iavf_virtchnl_done(adapter, condition, cond_data, v_opcode))
- ret = 0;
- else
- ret = -EAGAIN;
-
-out:
- kfree(event.msg_buf);
- return ret;
-}
--
2.53.0