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;