diff mbox series

[iwl-net,v10,1/5] iavf: Wait for reset in callbacks which trigger it

Message ID 20230605145226.1222225-2-mateusz.palczewski@intel.com
State Accepted
Delegated to: Anthony Nguyen
Headers show
Series iavf: fix reset task deadlock | expand

Commit Message

Mateusz Palczewski June 5, 2023, 2:52 p.m. UTC
From: Marcin Szycik <marcin.szycik@linux.intel.com>

There was a fail when trying to add the interface to bonding
right after changing the MTU on the interface. It was caused
by bonding interface unable to open the interface due to
interface being in __RESETTING state because of MTU change.

Add new reset_waitqueue to indicate that reset has finished.

Add waiting for reset to finish in callbacks which trigger hw reset:
iavf_set_priv_flags(), iavf_change_mtu() and iavf_set_ringparam().
We use a 5000ms timeout period because on Hyper-V based systems,
this operation takes around 3000-4000ms. In normal circumstances,
it doesn't take more than 500ms to complete.

Add a function iavf_wait_for_reset() to reuse waiting for reset code and
use it also in iavf_set_channels(), which already waits for reset.
We don't use error handling in iavf_set_channels() as this could
cause the device to be in incorrect state if the reset was scheduled
but hit timeout or the waitng function was interrupted by a signal.

Fixes: 4e5e6b5d9d13 ("iavf: Fix return of set the new channel count")
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Co-developed-by: Dawid Wesierski <dawidx.wesierski@intel.com>
Signed-off-by: Dawid Wesierski <dawidx.wesierski@intel.com>
Signed-off-by: Sylwester Dziedziuch <sylwesterx.dziedziuch@intel.com>
Signed-off-by: Kamil Maziarz <kamil.maziarz@intel.com>
Signed-off-by: Mateusz Palczewski <mateusz.palczewski@intel.com>
---
v1->v3: changes were done internally
v4: fixed SOB's
v5: changed the way we wake up the reset_waitqueue to make sure that reset is woken up
v6->v9: no changes
v10: Added fixes tag and fixed commit message
---
 drivers/net/ethernet/intel/iavf/iavf.h        |  2 +
 .../net/ethernet/intel/iavf/iavf_ethtool.c    | 31 ++++++-----
 drivers/net/ethernet/intel/iavf/iavf_main.c   | 51 ++++++++++++++++++-
 .../net/ethernet/intel/iavf/iavf_virtchnl.c   |  1 +
 4 files changed, 68 insertions(+), 17 deletions(-)

Comments

Romanowski, Rafal June 15, 2023, 3:17 p.m. UTC | #1
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of
> Mateusz Palczewski
> Sent: poniedziaƂek, 5 czerwca 2023 16:52
> To: intel-wired-lan@lists.osuosl.org
> Cc: ivecera <ivecera@redhat.com>; Maziarz, Kamil
> <kamil.maziarz@intel.com>; Sylwester Dziedziuch
> <sylwesterx.dziedziuch@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-net v10 1/5] iavf: Wait for reset in
> callbacks which trigger it
> 
> From: Marcin Szycik <marcin.szycik@linux.intel.com>
> 
> There was a fail when trying to add the interface to bonding right after
> changing the MTU on the interface. It was caused by bonding interface
> unable to open the interface due to interface being in __RESETTING state
> because of MTU change.
> 
> Add new reset_waitqueue to indicate that reset has finished.
> 
> Add waiting for reset to finish in callbacks which trigger hw reset:
> iavf_set_priv_flags(), iavf_change_mtu() and iavf_set_ringparam().
> We use a 5000ms timeout period because on Hyper-V based systems, this
> operation takes around 3000-4000ms. In normal circumstances, it doesn't
> take more than 500ms to complete.
> 
> Add a function iavf_wait_for_reset() to reuse waiting for reset code and use
> it also in iavf_set_channels(), which already waits for reset.
> We don't use error handling in iavf_set_channels() as this could cause the
> device to be in incorrect state if the reset was scheduled but hit timeout or
> the waitng function was interrupted by a signal.
> 
> Fixes: 4e5e6b5d9d13 ("iavf: Fix return of set the new channel count")
> Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
> Co-developed-by: Dawid Wesierski <dawidx.wesierski@intel.com>
> Signed-off-by: Dawid Wesierski <dawidx.wesierski@intel.com>
> Signed-off-by: Sylwester Dziedziuch <sylwesterx.dziedziuch@intel.com>
> Signed-off-by: Kamil Maziarz <kamil.maziarz@intel.com>
> Signed-off-by: Mateusz Palczewski <mateusz.palczewski@intel.com>
> ---
> v1->v3: changes were done internally
> v4: fixed SOB's
> v5: changed the way we wake up the reset_waitqueue to make sure that
> reset is woken up
> v6->v9: no changes
> v10: Added fixes tag and fixed commit message
> ---
>  drivers/net/ethernet/intel/iavf/iavf.h        |  2 +
>  .../net/ethernet/intel/iavf/iavf_ethtool.c    | 31 ++++++-----
>  drivers/net/ethernet/intel/iavf/iavf_main.c   | 51 ++++++++++++++++++-
>  .../net/ethernet/intel/iavf/iavf_virtchnl.c   |  1 +
>  4 files changed, 68 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iavf/iavf.h
> b/drivers/net/ethernet/intel/iavf/iavf.h
> index 9abaff1f2aff..c51b9ed4dc29 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf.h
> +++ b/drivers/net/ethernet/intel/iavf/iavf.h
> @@ -257,6 +257,7 @@ struct iavf_adapter {
>  	struct work_struct adminq_task;
>  	struct delayed_work client_task;
>  	wait_queue_head_t down_waitqueue;
> +	wait_queue_head_t reset_waitqueue;
>  	wait_queue_head_t vc_waitqueue;
>  	struct iavf_q_vector *q_vectors;
>  	struct list_head vlan_filter_list;
> @@ -591,5 +592,6 @@ void iavf_add_adv_rss_cfg(struct iavf_adapter
> *adapter);  void iavf_del_adv_rss_cfg(struct iavf_adapter *adapter);  struct
> iavf_mac_filter *iavf_add_filter(struct iavf_adapter *adapter,
>  					const u8 *macaddr);
> +int iavf_wait_for_reset(struct iavf_adapter *adapter);
>  int iavf_lock_timeout(struct mutex *lock, unsigned int msecs);  #endif /*
> _IAVF_H_ */ diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
> b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
> index 92443f8e9fbd..b7141c2a941d 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
> +++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
> @@ -484,6 +484,7 @@ static int iavf_set_priv_flags(struct net_device
> *netdev, u32 flags)  {
>  	struct iavf_adapter *adapter = netdev_priv(netdev);
>  	u32 orig_flags, new_flags, changed_flags;
> +	int ret = 0;
>  	u32 i;
> 
>  	orig_flags = READ_ONCE(adapter->flags); @@ -533,10 +534,13 @@
> static int iavf_set_priv_flags(struct net_device *netdev, u32 flags)
>  		if (netif_running(netdev)) {
>  			adapter->flags |= IAVF_FLAG_RESET_NEEDED;
>  			queue_work(adapter->wq, &adapter->reset_task);
> +			ret = iavf_wait_for_reset(adapter);
> +			if (ret)
> +				netdev_warn(netdev, "Changing private flags
> timeout or interrupted
> +waiting for reset");
>  		}
>  	}
> 
> -	return 0;
> +	return ret;
>  }
> 
>  /**
> @@ -627,6 +631,7 @@ static int iavf_set_ringparam(struct net_device
> *netdev,  {
>  	struct iavf_adapter *adapter = netdev_priv(netdev);
>  	u32 new_rx_count, new_tx_count;
> +	int ret = 0;
> 
>  	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
>  		return -EINVAL;
> @@ -673,9 +678,12 @@ static int iavf_set_ringparam(struct net_device
> *netdev,
>  	if (netif_running(netdev)) {
>  		adapter->flags |= IAVF_FLAG_RESET_NEEDED;
>  		queue_work(adapter->wq, &adapter->reset_task);
> +		ret = iavf_wait_for_reset(adapter);
> +		if (ret)
> +			netdev_warn(netdev, "Changing ring parameters
> timeout or interrupted
> +waiting for reset");
>  	}
> 
> -	return 0;
> +	return ret;
>  }
> 
>  /**
> @@ -1830,7 +1838,7 @@ static int iavf_set_channels(struct net_device
> *netdev,  {
>  	struct iavf_adapter *adapter = netdev_priv(netdev);
>  	u32 num_req = ch->combined_count;
> -	int i;
> +	int ret = 0;
> 
>  	if ((adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ)
> &&
>  	    adapter->num_tc) {
> @@ -1854,20 +1862,11 @@ static int iavf_set_channels(struct net_device
> *netdev,
>  	adapter->flags |= IAVF_FLAG_REINIT_ITR_NEEDED;
>  	iavf_schedule_reset(adapter);
> 
> -	/* wait for the reset is done */
> -	for (i = 0; i < IAVF_RESET_WAIT_COMPLETE_COUNT; i++) {
> -		msleep(IAVF_RESET_WAIT_MS);
> -		if (adapter->flags & IAVF_FLAG_RESET_PENDING)
> -			continue;
> -		break;
> -	}
> -	if (i == IAVF_RESET_WAIT_COMPLETE_COUNT) {
> -		adapter->flags &= ~IAVF_FLAG_REINIT_ITR_NEEDED;
> -		adapter->num_req_queues = 0;
> -		return -EOPNOTSUPP;
> -	}
> +	ret = iavf_wait_for_reset(adapter);
> +	if (ret)
> +		netdev_warn(netdev, "Changing channel count timeout or
> interrupted
> +waiting for reset");
> 
> -	return 0;
> +	return ret;
>  }
> 
>  /**
> diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c
> b/drivers/net/ethernet/intel/iavf/iavf_main.c
> index 89892a0fd5b7..c815ef87e27d 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf_main.c
> +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
> @@ -166,6 +166,45 @@ static struct iavf_adapter
> *iavf_pdev_to_adapter(struct pci_dev *pdev)
>  	return netdev_priv(pci_get_drvdata(pdev));
>  }
> 
> +/**
> + * iavf_is_reset_in_progress - Check if a reset is in progress
> + * @adapter: board private structure
> + */
> +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_wait_for_reset - Wait for reset to finish.
> + * @adapter: board private structure
> + *
> + * Returns 0 if reset finished successfully, negative on timeout or interrupt.
> + */
> +int iavf_wait_for_reset(struct iavf_adapter *adapter) {
> +	int ret = wait_event_interruptible_timeout(adapter-
> >reset_waitqueue,
> +					!iavf_is_reset_in_progress(adapter),
> +					msecs_to_jiffies(5000));
> +
> +	/* If ret < 0 then it means wait was interrupted.
> +	 * If ret == 0 then it means we got a timeout while waiting
> +	 * for reset to finish.
> +	 * If ret > 0 it means reset has finished.
> +	 */
> +	if (ret > 0)
> +		return 0;
> +	else if (ret < 0)
> +		return -EINTR;
> +	else
> +		return -EBUSY;
> +}
> +
>  /**
>   * iavf_allocate_dma_mem_d - OS specific memory alloc for shared code
>   * @hw:   pointer to the HW structure
> @@ -3163,6 +3202,7 @@ static void iavf_reset_task(struct work_struct
> *work)
> 
>  	adapter->flags &= ~IAVF_FLAG_REINIT_ITR_NEEDED;
> 
> +	wake_up(&adapter->reset_waitqueue);
>  	mutex_unlock(&adapter->client_lock);
>  	mutex_unlock(&adapter->crit_lock);
> 
> @@ -4327,6 +4367,7 @@ static int iavf_close(struct net_device *netdev)
> static int iavf_change_mtu(struct net_device *netdev, int new_mtu)  {
>  	struct iavf_adapter *adapter = netdev_priv(netdev);
> +	int ret = 0;
> 
>  	netdev_dbg(netdev, "changing MTU from %d to %d\n",
>  		   netdev->mtu, new_mtu);
> @@ -4339,9 +4380,14 @@ static int iavf_change_mtu(struct net_device
> *netdev, int new_mtu)
>  	if (netif_running(netdev)) {
>  		adapter->flags |= IAVF_FLAG_RESET_NEEDED;
>  		queue_work(adapter->wq, &adapter->reset_task);
> +		ret = iavf_wait_for_reset(adapter);
> +		if (ret < 0)
> +			netdev_warn(netdev, "MTU change interrupted
> waiting for reset");
> +		else if (ret)
> +			netdev_warn(netdev, "MTU change timed out
> waiting for reset");
>  	}
> 
> -	return 0;
> +	return ret;
>  }
> 
>  #define NETIF_VLAN_OFFLOAD_FEATURES
> 	(NETIF_F_HW_VLAN_CTAG_RX | \
> @@ -4942,6 +4988,9 @@ 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 transition to running state */
> +	init_waitqueue_head(&adapter->reset_waitqueue);
> +
>  	/* Setup the wait queue for indicating virtchannel events */
>  	init_waitqueue_head(&adapter->vc_waitqueue);
> 
> diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
> b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
> index 7c0578b5457b..1bab896aaf40 100644
> --- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
> @@ -2285,6 +2285,7 @@ void iavf_virtchnl_completion(struct iavf_adapter
> *adapter,
>  	case VIRTCHNL_OP_ENABLE_QUEUES:
>  		/* enable transmits */
>  		iavf_irq_enable(adapter, true);
> +		wake_up(&adapter->reset_waitqueue);
>  		adapter->flags &= ~IAVF_FLAG_QUEUES_DISABLED;
>  		break;
>  	case VIRTCHNL_OP_DISABLE_QUEUES:
> --
> 2.31.1
> 
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan@osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
index 9abaff1f2aff..c51b9ed4dc29 100644
--- a/drivers/net/ethernet/intel/iavf/iavf.h
+++ b/drivers/net/ethernet/intel/iavf/iavf.h
@@ -257,6 +257,7 @@  struct iavf_adapter {
 	struct work_struct adminq_task;
 	struct delayed_work client_task;
 	wait_queue_head_t down_waitqueue;
+	wait_queue_head_t reset_waitqueue;
 	wait_queue_head_t vc_waitqueue;
 	struct iavf_q_vector *q_vectors;
 	struct list_head vlan_filter_list;
@@ -591,5 +592,6 @@  void iavf_add_adv_rss_cfg(struct iavf_adapter *adapter);
 void iavf_del_adv_rss_cfg(struct iavf_adapter *adapter);
 struct iavf_mac_filter *iavf_add_filter(struct iavf_adapter *adapter,
 					const u8 *macaddr);
+int iavf_wait_for_reset(struct iavf_adapter *adapter);
 int iavf_lock_timeout(struct mutex *lock, unsigned int msecs);
 #endif /* _IAVF_H_ */
diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
index 92443f8e9fbd..b7141c2a941d 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
@@ -484,6 +484,7 @@  static int iavf_set_priv_flags(struct net_device *netdev, u32 flags)
 {
 	struct iavf_adapter *adapter = netdev_priv(netdev);
 	u32 orig_flags, new_flags, changed_flags;
+	int ret = 0;
 	u32 i;
 
 	orig_flags = READ_ONCE(adapter->flags);
@@ -533,10 +534,13 @@  static int iavf_set_priv_flags(struct net_device *netdev, u32 flags)
 		if (netif_running(netdev)) {
 			adapter->flags |= IAVF_FLAG_RESET_NEEDED;
 			queue_work(adapter->wq, &adapter->reset_task);
+			ret = iavf_wait_for_reset(adapter);
+			if (ret)
+				netdev_warn(netdev, "Changing private flags timeout or interrupted waiting for reset");
 		}
 	}
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -627,6 +631,7 @@  static int iavf_set_ringparam(struct net_device *netdev,
 {
 	struct iavf_adapter *adapter = netdev_priv(netdev);
 	u32 new_rx_count, new_tx_count;
+	int ret = 0;
 
 	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
 		return -EINVAL;
@@ -673,9 +678,12 @@  static int iavf_set_ringparam(struct net_device *netdev,
 	if (netif_running(netdev)) {
 		adapter->flags |= IAVF_FLAG_RESET_NEEDED;
 		queue_work(adapter->wq, &adapter->reset_task);
+		ret = iavf_wait_for_reset(adapter);
+		if (ret)
+			netdev_warn(netdev, "Changing ring parameters timeout or interrupted waiting for reset");
 	}
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -1830,7 +1838,7 @@  static int iavf_set_channels(struct net_device *netdev,
 {
 	struct iavf_adapter *adapter = netdev_priv(netdev);
 	u32 num_req = ch->combined_count;
-	int i;
+	int ret = 0;
 
 	if ((adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ADQ) &&
 	    adapter->num_tc) {
@@ -1854,20 +1862,11 @@  static int iavf_set_channels(struct net_device *netdev,
 	adapter->flags |= IAVF_FLAG_REINIT_ITR_NEEDED;
 	iavf_schedule_reset(adapter);
 
-	/* wait for the reset is done */
-	for (i = 0; i < IAVF_RESET_WAIT_COMPLETE_COUNT; i++) {
-		msleep(IAVF_RESET_WAIT_MS);
-		if (adapter->flags & IAVF_FLAG_RESET_PENDING)
-			continue;
-		break;
-	}
-	if (i == IAVF_RESET_WAIT_COMPLETE_COUNT) {
-		adapter->flags &= ~IAVF_FLAG_REINIT_ITR_NEEDED;
-		adapter->num_req_queues = 0;
-		return -EOPNOTSUPP;
-	}
+	ret = iavf_wait_for_reset(adapter);
+	if (ret)
+		netdev_warn(netdev, "Changing channel count timeout or interrupted waiting for reset");
 
-	return 0;
+	return ret;
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c
index 89892a0fd5b7..c815ef87e27d 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_main.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
@@ -166,6 +166,45 @@  static struct iavf_adapter *iavf_pdev_to_adapter(struct pci_dev *pdev)
 	return netdev_priv(pci_get_drvdata(pdev));
 }
 
+/**
+ * iavf_is_reset_in_progress - Check if a reset is in progress
+ * @adapter: board private structure
+ */
+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_wait_for_reset - Wait for reset to finish.
+ * @adapter: board private structure
+ *
+ * Returns 0 if reset finished successfully, negative on timeout or interrupt.
+ */
+int iavf_wait_for_reset(struct iavf_adapter *adapter)
+{
+	int ret = wait_event_interruptible_timeout(adapter->reset_waitqueue,
+					!iavf_is_reset_in_progress(adapter),
+					msecs_to_jiffies(5000));
+
+	/* If ret < 0 then it means wait was interrupted.
+	 * If ret == 0 then it means we got a timeout while waiting
+	 * for reset to finish.
+	 * If ret > 0 it means reset has finished.
+	 */
+	if (ret > 0)
+		return 0;
+	else if (ret < 0)
+		return -EINTR;
+	else
+		return -EBUSY;
+}
+
 /**
  * iavf_allocate_dma_mem_d - OS specific memory alloc for shared code
  * @hw:   pointer to the HW structure
@@ -3163,6 +3202,7 @@  static void iavf_reset_task(struct work_struct *work)
 
 	adapter->flags &= ~IAVF_FLAG_REINIT_ITR_NEEDED;
 
+	wake_up(&adapter->reset_waitqueue);
 	mutex_unlock(&adapter->client_lock);
 	mutex_unlock(&adapter->crit_lock);
 
@@ -4327,6 +4367,7 @@  static int iavf_close(struct net_device *netdev)
 static int iavf_change_mtu(struct net_device *netdev, int new_mtu)
 {
 	struct iavf_adapter *adapter = netdev_priv(netdev);
+	int ret = 0;
 
 	netdev_dbg(netdev, "changing MTU from %d to %d\n",
 		   netdev->mtu, new_mtu);
@@ -4339,9 +4380,14 @@  static int iavf_change_mtu(struct net_device *netdev, int new_mtu)
 	if (netif_running(netdev)) {
 		adapter->flags |= IAVF_FLAG_RESET_NEEDED;
 		queue_work(adapter->wq, &adapter->reset_task);
+		ret = iavf_wait_for_reset(adapter);
+		if (ret < 0)
+			netdev_warn(netdev, "MTU change interrupted waiting for reset");
+		else if (ret)
+			netdev_warn(netdev, "MTU change timed out waiting for reset");
 	}
 
-	return 0;
+	return ret;
 }
 
 #define NETIF_VLAN_OFFLOAD_FEATURES	(NETIF_F_HW_VLAN_CTAG_RX | \
@@ -4942,6 +4988,9 @@  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 transition to running state */
+	init_waitqueue_head(&adapter->reset_waitqueue);
+
 	/* Setup the wait queue for indicating virtchannel events */
 	init_waitqueue_head(&adapter->vc_waitqueue);
 
diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
index 7c0578b5457b..1bab896aaf40 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
@@ -2285,6 +2285,7 @@  void iavf_virtchnl_completion(struct iavf_adapter *adapter,
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 		/* enable transmits */
 		iavf_irq_enable(adapter, true);
+		wake_up(&adapter->reset_waitqueue);
 		adapter->flags &= ~IAVF_FLAG_QUEUES_DISABLED;
 		break;
 	case VIRTCHNL_OP_DISABLE_QUEUES: