]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
i40evf: prevent VF close returning before state transitions to DOWN
authorSudheer Mogilappagari <sudheer.mogilappagari@intel.com>
Fri, 23 Jun 2017 08:24:44 +0000 (04:24 -0400)
committerJack Vogel <jack.vogel@oracle.com>
Tue, 10 Oct 2017 21:15:25 +0000 (14:15 -0700)
Currently i40evf_close() can return before state transitions to
__I40EVF_DOWN because of the latency involved in processing and
receiving response from PF driver and scheduling of VF watchdog_task.
Due to this inconsistency an immediate call to i40evf_open() fails
because state is still DOWN_PENDING.

When a VF interface is in up state and we try to add it as slave,
The bonding driver calls dev_close() and dev_open() in short duration
resulting in dev_open returning error. The ifenslave command needs
to be run again for dev_open to succeed.

This fix ensures that watchdog timer is scheduled immediately after
admin queue operations are scheduled in i40evf_down(). In addition a
wait condition is added at the end of i40evf_close so that function
wont return when state is still DOWN_PENDING. The timeout value is
chosen after some profiling and includes some buffer.

Signed-off-by: Sudheer Mogilappagari <sudheer.mogilappagari@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Orabug: 26785018
(cherry picked from commit fe2647ab0c9970cfc2895f1671343c23fed27f44)
Signed-off-by: Jack Vogel <jack.vogel@oracle.com>
Reviewed-by: Kyle Fortin <kyle.fortin@oracle.com>
drivers/net/ethernet/intel/i40evf/i40evf.h
drivers/net/ethernet/intel/i40evf/i40evf_main.c
drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c

index eb554deb8d025be838e4932cdf41ba7932e1b67a..dc97ea91b2564093bd144b9e63cb1c3c8738af9b 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/bitops.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
+#include <linux/wait.h>
 #include <linux/delay.h>
 #include <linux/gfp.h>
 #include <linux/skbuff.h>
@@ -189,6 +190,7 @@ struct i40evf_adapter {
        struct work_struct adminq_task;
        struct delayed_work client_task;
        struct delayed_work init_task;
+       wait_queue_head_t down_waitqueue;
        struct i40e_q_vector *q_vectors;
        struct list_head vlan_filter_list;
        char misc_vector_name[IFNAMSIZ + 9];
index 0261047610811b853eb319c7d3c3bc12b23f5637..04e76ec4f218ee14dde51bd70f588c8ef5c29728 100644 (file)
@@ -1132,6 +1132,7 @@ void i40evf_down(struct i40evf_adapter *adapter)
        }
 
        clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
+       mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
 }
 
 /**
@@ -1783,6 +1784,7 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter)
        clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
        adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
        adapter->state = __I40EVF_DOWN;
+       wake_up(&adapter->down_waitqueue);
        dev_info(&adapter->pdev->dev, "Reset task did not complete, VF disabled\n");
 }
 
@@ -1928,6 +1930,7 @@ continue_reset:
                i40evf_irq_enable(adapter, true);
        } else {
                adapter->state = __I40EVF_DOWN;
+               wake_up(&adapter->down_waitqueue);
        }
 
        return;
@@ -2228,6 +2231,7 @@ err_setup_tx:
 static int i40evf_close(struct net_device *netdev)
 {
        struct i40evf_adapter *adapter = netdev_priv(netdev);
+       int status;
 
        if (adapter->state <= __I40EVF_DOWN_PENDING)
                return 0;
@@ -2245,7 +2249,18 @@ static int i40evf_close(struct net_device *netdev)
         * still active and can DMA into memory. Resources are cleared in
         * i40evf_virtchnl_completion() after we get confirmation from the PF
         * driver that the rings have been stopped.
+        *
+        * Also, we wait for state to transition to __I40EVF_DOWN before
+        * returning. State change occurs in i40evf_virtchnl_completion() after
+        * VF resources are released (which occurs after PF driver processes and
+        * responds to admin queue commands).
         */
+
+       status = wait_event_timeout(adapter->down_waitqueue,
+                                   adapter->state == __I40EVF_DOWN,
+                                   msecs_to_jiffies(200));
+       if (!status)
+               netdev_warn(netdev, "Device resources not yet released\n");
        return 0;
 }
 
@@ -2661,6 +2676,7 @@ static void i40evf_init_task(struct work_struct *work)
        adapter->state = __I40EVF_DOWN;
        set_bit(__I40E_DOWN, &adapter->vsi.state);
        i40evf_misc_irq_enable(adapter);
+       wake_up(&adapter->down_waitqueue);
 
        adapter->rss_key = kzalloc(adapter->rss_key_size, GFP_KERNEL);
        adapter->rss_lut = kzalloc(adapter->rss_lut_size, GFP_KERNEL);
@@ -2822,6 +2838,9 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        schedule_delayed_work(&adapter->init_task,
                              msecs_to_jiffies(5 * (pdev->devfn & 0x07)));
 
+       /* Setup the wait queue for indicating transition to down status */
+       init_waitqueue_head(&adapter->down_waitqueue);
+
        return 0;
 
 err_ioremap:
index fe3e70d6468058fbe322742e6bc6970988c17413..60f5b9b93b63bb2e3fc02530ccf84f66a922c031 100644 (file)
@@ -989,8 +989,10 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
        case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
                i40evf_free_all_tx_resources(adapter);
                i40evf_free_all_rx_resources(adapter);
-               if (adapter->state == __I40EVF_DOWN_PENDING)
+               if (adapter->state == __I40EVF_DOWN_PENDING) {
                        adapter->state = __I40EVF_DOWN;
+                       wake_up(&adapter->down_waitqueue);
+               }
                break;
        case I40E_VIRTCHNL_OP_VERSION:
        case I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP: