|
Thomas Bogendoerfer |
70d0a1 |
From: Jacob Keller <jacob.e.keller@intel.com>
|
|
Thomas Bogendoerfer |
70d0a1 |
Date: Fri, 27 Oct 2017 11:06:52 -0400
|
|
Thomas Bogendoerfer |
70d0a1 |
Subject: i40evf: hold the critical task bit lock while opening
|
|
Thomas Bogendoerfer |
70d0a1 |
Patch-mainline: v4.16-rc1
|
|
Thomas Bogendoerfer |
70d0a1 |
Git-commit: 9b2aef128bff371c1b91b492a92cf42dad8c739b
|
|
Thomas Bogendoerfer |
70d0a1 |
References: bsc#1101816 FATE#325147 FATE#325149
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
If i40evf_open() is called quickly at the same time as a reset occurs
|
|
Thomas Bogendoerfer |
70d0a1 |
(such as via ethtool) it is possible for the device to attempt to open
|
|
Thomas Bogendoerfer |
70d0a1 |
while a reset is in progress. This occurs because the driver was not
|
|
Thomas Bogendoerfer |
70d0a1 |
holding the critical task bit lock during i40evf_open, nor was it
|
|
Thomas Bogendoerfer |
70d0a1 |
holding it around the call to i40evf_up_complete() in
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_reset_task().
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
We didn't hold the lock previously because calls to i40evf_down() would
|
|
Thomas Bogendoerfer |
70d0a1 |
take the bit lock directly, and this would have caused a deadlock.
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
To avoid this, we'll move the bit lock handling out of i40evf_down() and
|
|
Thomas Bogendoerfer |
70d0a1 |
into the callers of this function. Additionally, we'll now hold the bit
|
|
Thomas Bogendoerfer |
70d0a1 |
lock over the entire set of steps when going up or down, to ensure that
|
|
Thomas Bogendoerfer |
70d0a1 |
we remain consistent.
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
Ultimately this causes us to serialize the transitions between down and
|
|
Thomas Bogendoerfer |
70d0a1 |
up properly, and avoid changing status while we're resetting.
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
|
|
Thomas Bogendoerfer |
70d0a1 |
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
|
|
Thomas Bogendoerfer |
70d0a1 |
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
|
|
Thomas Bogendoerfer |
70d0a1 |
Acked-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
|
|
Thomas Bogendoerfer |
70d0a1 |
---
|
|
Thomas Bogendoerfer |
70d0a1 |
drivers/net/ethernet/intel/i40evf/i40evf_main.c | 40 ++++++++++++++++++------
|
|
Thomas Bogendoerfer |
70d0a1 |
1 file changed, 31 insertions(+), 9 deletions(-)
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
|
|
Thomas Bogendoerfer |
70d0a1 |
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -1035,6 +1035,8 @@ static void i40evf_configure(struct i40e
|
|
Thomas Bogendoerfer |
70d0a1 |
/**
|
|
Thomas Bogendoerfer |
70d0a1 |
* i40evf_up_complete - Finish the last steps of bringing up a connection
|
|
Thomas Bogendoerfer |
70d0a1 |
* @adapter: board private structure
|
|
Thomas Bogendoerfer |
70d0a1 |
+ *
|
|
Thomas Bogendoerfer |
70d0a1 |
+ * Expects to be called while holding the __I40EVF_IN_CRITICAL_TASK bit lock.
|
|
Thomas Bogendoerfer |
70d0a1 |
**/
|
|
Thomas Bogendoerfer |
70d0a1 |
static void i40evf_up_complete(struct i40evf_adapter *adapter)
|
|
Thomas Bogendoerfer |
70d0a1 |
{
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -1052,6 +1054,8 @@ static void i40evf_up_complete(struct i4
|
|
Thomas Bogendoerfer |
70d0a1 |
/**
|
|
Thomas Bogendoerfer |
70d0a1 |
* i40e_down - Shutdown the connection processing
|
|
Thomas Bogendoerfer |
70d0a1 |
* @adapter: board private structure
|
|
Thomas Bogendoerfer |
70d0a1 |
+ *
|
|
Thomas Bogendoerfer |
70d0a1 |
+ * Expects to be called while holding the __I40EVF_IN_CRITICAL_TASK bit lock.
|
|
Thomas Bogendoerfer |
70d0a1 |
**/
|
|
Thomas Bogendoerfer |
70d0a1 |
void i40evf_down(struct i40evf_adapter *adapter)
|
|
Thomas Bogendoerfer |
70d0a1 |
{
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -1061,10 +1065,6 @@ void i40evf_down(struct i40evf_adapter *
|
|
Thomas Bogendoerfer |
70d0a1 |
if (adapter->state <= __I40EVF_DOWN_PENDING)
|
|
Thomas Bogendoerfer |
70d0a1 |
return;
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
- while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
|
|
Thomas Bogendoerfer |
70d0a1 |
- &adapter->crit_section))
|
|
Thomas Bogendoerfer |
70d0a1 |
- usleep_range(500, 1000);
|
|
Thomas Bogendoerfer |
70d0a1 |
-
|
|
Thomas Bogendoerfer |
70d0a1 |
netif_carrier_off(netdev);
|
|
Thomas Bogendoerfer |
70d0a1 |
netif_tx_disable(netdev);
|
|
Thomas Bogendoerfer |
70d0a1 |
adapter->link_up = false;
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -1097,7 +1097,6 @@ void i40evf_down(struct i40evf_adapter *
|
|
Thomas Bogendoerfer |
70d0a1 |
adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES;
|
|
Thomas Bogendoerfer |
70d0a1 |
}
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
- clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
|
|
Thomas Bogendoerfer |
70d0a1 |
}
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -1959,8 +1958,6 @@ continue_reset:
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
adapter->aq_required |= I40EVF_FLAG_AQ_ADD_MAC_FILTER;
|
|
Thomas Bogendoerfer |
70d0a1 |
adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
|
|
Thomas Bogendoerfer |
70d0a1 |
- clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
- clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_misc_irq_enable(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
mod_timer(&adapter->watchdog_timer, jiffies + 2);
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -1997,9 +1994,13 @@ continue_reset:
|
|
Thomas Bogendoerfer |
70d0a1 |
adapter->state = __I40EVF_DOWN;
|
|
Thomas Bogendoerfer |
70d0a1 |
wake_up(&adapter->down_waitqueue);
|
|
Thomas Bogendoerfer |
70d0a1 |
}
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
return;
|
|
Thomas Bogendoerfer |
70d0a1 |
reset_err:
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n");
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_close(netdev);
|
|
Thomas Bogendoerfer |
70d0a1 |
}
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -2243,8 +2244,14 @@ static int i40evf_open(struct net_device
|
|
Thomas Bogendoerfer |
70d0a1 |
return -EIO;
|
|
Thomas Bogendoerfer |
70d0a1 |
}
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
- if (adapter->state != __I40EVF_DOWN)
|
|
Thomas Bogendoerfer |
70d0a1 |
- return -EBUSY;
|
|
Thomas Bogendoerfer |
70d0a1 |
+ while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
|
|
Thomas Bogendoerfer |
70d0a1 |
+ &adapter->crit_section))
|
|
Thomas Bogendoerfer |
70d0a1 |
+ usleep_range(500, 1000);
|
|
Thomas Bogendoerfer |
70d0a1 |
+
|
|
Thomas Bogendoerfer |
70d0a1 |
+ if (adapter->state != __I40EVF_DOWN) {
|
|
Thomas Bogendoerfer |
70d0a1 |
+ err = -EBUSY;
|
|
Thomas Bogendoerfer |
70d0a1 |
+ goto err_unlock;
|
|
Thomas Bogendoerfer |
70d0a1 |
+ }
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
/* allocate transmit descriptors */
|
|
Thomas Bogendoerfer |
70d0a1 |
err = i40evf_setup_all_tx_resources(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -2268,6 +2275,8 @@ static int i40evf_open(struct net_device
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_irq_enable(adapter, true);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
+
|
|
Thomas Bogendoerfer |
70d0a1 |
return 0;
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
err_req_irq:
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -2277,6 +2286,8 @@ err_setup_rx:
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_free_all_rx_resources(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
err_setup_tx:
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_free_all_tx_resources(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
+err_unlock:
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
return err;
|
|
Thomas Bogendoerfer |
70d0a1 |
}
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -2300,6 +2311,9 @@ static int i40evf_close(struct net_devic
|
|
Thomas Bogendoerfer |
70d0a1 |
if (adapter->state <= __I40EVF_DOWN_PENDING)
|
|
Thomas Bogendoerfer |
70d0a1 |
return 0;
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
+ while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
|
|
Thomas Bogendoerfer |
70d0a1 |
+ &adapter->crit_section))
|
|
Thomas Bogendoerfer |
70d0a1 |
+ usleep_range(500, 1000);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
set_bit(__I40E_VSI_DOWN, adapter->vsi.state);
|
|
Thomas Bogendoerfer |
70d0a1 |
if (CLIENT_ENABLED(adapter))
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -2309,6 +2323,8 @@ static int i40evf_close(struct net_devic
|
|
Thomas Bogendoerfer |
70d0a1 |
adapter->state = __I40EVF_DOWN_PENDING;
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_free_traffic_irqs(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
+
|
|
Thomas Bogendoerfer |
70d0a1 |
/* We explicitly don't free resources here because the hardware is
|
|
Thomas Bogendoerfer |
70d0a1 |
* still active and can DMA into memory. Resources are cleared in
|
|
Thomas Bogendoerfer |
70d0a1 |
* i40evf_virtchnl_completion() after we get confirmation from the PF
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -2992,6 +3008,10 @@ static int i40evf_suspend(struct pci_dev
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
netif_device_detach(netdev);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
+ while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
|
|
Thomas Bogendoerfer |
70d0a1 |
+ &adapter->crit_section))
|
|
Thomas Bogendoerfer |
70d0a1 |
+ usleep_range(500, 1000);
|
|
Thomas Bogendoerfer |
70d0a1 |
+
|
|
Thomas Bogendoerfer |
70d0a1 |
if (netif_running(netdev)) {
|
|
Thomas Bogendoerfer |
70d0a1 |
rtnl_lock();
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_down(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
@@ -3000,6 +3020,8 @@ static int i40evf_suspend(struct pci_dev
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_free_misc_irq(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
i40evf_reset_interrupt_capability(adapter);
|
|
Thomas Bogendoerfer |
70d0a1 |
|
|
Thomas Bogendoerfer |
70d0a1 |
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
|
|
Thomas Bogendoerfer |
70d0a1 |
+
|
|
Thomas Bogendoerfer |
70d0a1 |
retval = pci_save_state(pdev);
|
|
Thomas Bogendoerfer |
70d0a1 |
if (retval)
|
|
Thomas Bogendoerfer |
70d0a1 |
return retval;
|