Skip to content

Commit

Permalink
iavf: Wait for reset in callbacks which trigger it
Browse files Browse the repository at this point in the history
BugLink: https://bugs.launchpad.net/bugs/2037005

[ Upstream commit c2ed240 ]

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: 4e5e6b5 ("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>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Roxana Nicolescu <roxana.nicolescu@canonical.com>
  • Loading branch information
Marcin Szycik authored and roxanan1996 committed Oct 2, 2023
1 parent be55d97 commit 64f0d84
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 17 deletions.
2 changes: 2 additions & 0 deletions drivers/net/ethernet/intel/iavf/iavf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -582,4 +583,5 @@ 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);
#endif /* _IAVF_H_ */
31 changes: 15 additions & 16 deletions drivers/net/ethernet/intel/iavf/iavf_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}

/**
Expand Down
51 changes: 50 additions & 1 deletion drivers/net/ethernet/intel/iavf/iavf_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -3161,6 +3200,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);

Expand Down Expand Up @@ -4325,6 +4365,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);
Expand All @@ -4337,9 +4378,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 | \
Expand Down Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 64f0d84

Please sign in to comment.