ibmvnic: Introduce hard reset recovery
authorThomas Falcon <tlfalcon@linux.vnet.ibm.com>
Wed, 23 May 2018 18:38:02 +0000 (13:38 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 25 May 2018 02:19:26 +0000 (22:19 -0400)
Introduce a recovery hard reset to handle reset failure as a result of
change of device context following a transport event, such as a
backing device failover or partition migration. These operations reset
the device context to its initial state. If this occurs during a reset,
any initialization commands are likely to fail with an invalid state
error as backing device firmware requests reinitialization.

When this happens, make one more attempt by performing a hard reset,
which frees any resources currently allocated and performs device
initialization. If a transport event occurs during a device reset, a
flag is set which will trigger a new hard reset following the
completionof the current reset event.

Signed-off-by: Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/ibm/ibmvnic.h

index ee51deb..09f8e6b 100644 (file)
@@ -1878,6 +1878,85 @@ static int do_reset(struct ibmvnic_adapter *adapter,
        return 0;
 }
 
+static int do_hard_reset(struct ibmvnic_adapter *adapter,
+                        struct ibmvnic_rwi *rwi, u32 reset_state)
+{
+       struct net_device *netdev = adapter->netdev;
+       int rc;
+
+       netdev_dbg(adapter->netdev, "Hard resetting driver (%d)\n",
+                  rwi->reset_reason);
+
+       netif_carrier_off(netdev);
+       adapter->reset_reason = rwi->reset_reason;
+
+       ibmvnic_cleanup(netdev);
+       release_resources(adapter);
+       release_sub_crqs(adapter, 0);
+       release_crq_queue(adapter);
+
+       /* remove the closed state so when we call open it appears
+        * we are coming from the probed state.
+        */
+       adapter->state = VNIC_PROBED;
+
+       rc = init_crq_queue(adapter);
+       if (rc) {
+               netdev_err(adapter->netdev,
+                          "Couldn't initialize crq. rc=%d\n", rc);
+               return rc;
+       }
+
+       rc = ibmvnic_init(adapter);
+       if (rc)
+               return rc;
+
+       /* If the adapter was in PROBE state prior to the reset,
+        * exit here.
+        */
+       if (reset_state == VNIC_PROBED)
+               return 0;
+
+       rc = ibmvnic_login(netdev);
+       if (rc) {
+               adapter->state = VNIC_PROBED;
+               return 0;
+       }
+       /* netif_set_real_num_xx_queues needs to take rtnl lock here
+        * unless wait_for_reset is set, in which case the rtnl lock
+        * has already been taken before initializing the reset
+        */
+       if (!adapter->wait_for_reset) {
+               rtnl_lock();
+               rc = init_resources(adapter);
+               rtnl_unlock();
+       } else {
+               rc = init_resources(adapter);
+       }
+       if (rc)
+               return rc;
+
+       ibmvnic_disable_irqs(adapter);
+       adapter->state = VNIC_CLOSED;
+
+       if (reset_state == VNIC_CLOSED)
+               return 0;
+
+       rc = __ibmvnic_open(netdev);
+       if (rc) {
+               if (list_empty(&adapter->rwi_list))
+                       adapter->state = VNIC_CLOSED;
+               else
+                       adapter->state = reset_state;
+
+               return 0;
+       }
+
+       netif_carrier_on(netdev);
+
+       return 0;
+}
+
 static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter)
 {
        struct ibmvnic_rwi *rwi;
@@ -1923,9 +2002,15 @@ static void __ibmvnic_reset(struct work_struct *work)
 
        rwi = get_next_rwi(adapter);
        while (rwi) {
-               rc = do_reset(adapter, rwi, reset_state);
+               if (adapter->force_reset_recovery) {
+                       adapter->force_reset_recovery = false;
+                       rc = do_hard_reset(adapter, rwi, reset_state);
+               } else {
+                       rc = do_reset(adapter, rwi, reset_state);
+               }
                kfree(rwi);
-               if (rc && rc != IBMVNIC_INIT_FAILED)
+               if (rc && rc != IBMVNIC_INIT_FAILED &&
+                   !adapter->force_reset_recovery)
                        break;
 
                rwi = get_next_rwi(adapter);
@@ -1951,9 +2036,9 @@ static void __ibmvnic_reset(struct work_struct *work)
 static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
                         enum ibmvnic_reset_reason reason)
 {
+       struct list_head *entry, *tmp_entry;
        struct ibmvnic_rwi *rwi, *tmp;
        struct net_device *netdev = adapter->netdev;
-       struct list_head *entry;
        int ret;
 
        if (adapter->state == VNIC_REMOVING ||
@@ -1989,7 +2074,13 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
                ret = ENOMEM;
                goto err;
        }
-
+       /* if we just received a transport event,
+        * flush reset queue and process this reset
+        */
+       if (adapter->force_reset_recovery && !list_empty(&adapter->rwi_list)) {
+               list_for_each_safe(entry, tmp_entry, &adapter->rwi_list)
+                       list_del(entry);
+       }
        rwi->reset_reason = reason;
        list_add_tail(&rwi->list, &adapter->rwi_list);
        mutex_unlock(&adapter->rwi_lock);
@@ -4271,6 +4362,8 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
        case IBMVNIC_CRQ_XPORT_EVENT:
                netif_carrier_off(netdev);
                adapter->crq.active = false;
+               if (adapter->resetting)
+                       adapter->force_reset_recovery = true;
                if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) {
                        dev_info(dev, "Migrated, re-enabling adapter\n");
                        ibmvnic_reset(adapter, VNIC_RESET_MOBILITY);
index edfc312..f9fb780 100644 (file)
@@ -1109,6 +1109,7 @@ struct ibmvnic_adapter {
 
        bool mac_change_pending;
        bool failover_pending;
+       bool force_reset_recovery;
 
        struct ibmvnic_tunables desired;
        struct ibmvnic_tunables fallback;