Commit 2770a798 authored by Thomas Falcon's avatar Thomas Falcon Committed by David S. Miller

ibmvnic: Introduce hard reset recovery

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: default avatarThomas Falcon <tlfalcon@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 06e43d7f
...@@ -1878,6 +1878,85 @@ static int do_reset(struct ibmvnic_adapter *adapter, ...@@ -1878,6 +1878,85 @@ static int do_reset(struct ibmvnic_adapter *adapter,
return 0; 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) static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter)
{ {
struct ibmvnic_rwi *rwi; struct ibmvnic_rwi *rwi;
...@@ -1923,9 +2002,15 @@ static void __ibmvnic_reset(struct work_struct *work) ...@@ -1923,9 +2002,15 @@ static void __ibmvnic_reset(struct work_struct *work)
rwi = get_next_rwi(adapter); rwi = get_next_rwi(adapter);
while (rwi) { 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); kfree(rwi);
if (rc && rc != IBMVNIC_INIT_FAILED) if (rc && rc != IBMVNIC_INIT_FAILED &&
!adapter->force_reset_recovery)
break; break;
rwi = get_next_rwi(adapter); rwi = get_next_rwi(adapter);
...@@ -1951,9 +2036,9 @@ static void __ibmvnic_reset(struct work_struct *work) ...@@ -1951,9 +2036,9 @@ static void __ibmvnic_reset(struct work_struct *work)
static int ibmvnic_reset(struct ibmvnic_adapter *adapter, static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
enum ibmvnic_reset_reason reason) enum ibmvnic_reset_reason reason)
{ {
struct list_head *entry, *tmp_entry;
struct ibmvnic_rwi *rwi, *tmp; struct ibmvnic_rwi *rwi, *tmp;
struct net_device *netdev = adapter->netdev; struct net_device *netdev = adapter->netdev;
struct list_head *entry;
int ret; int ret;
if (adapter->state == VNIC_REMOVING || if (adapter->state == VNIC_REMOVING ||
...@@ -1989,7 +2074,13 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter, ...@@ -1989,7 +2074,13 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
ret = ENOMEM; ret = ENOMEM;
goto err; 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; rwi->reset_reason = reason;
list_add_tail(&rwi->list, &adapter->rwi_list); list_add_tail(&rwi->list, &adapter->rwi_list);
mutex_unlock(&adapter->rwi_lock); mutex_unlock(&adapter->rwi_lock);
...@@ -4271,6 +4362,8 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, ...@@ -4271,6 +4362,8 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
case IBMVNIC_CRQ_XPORT_EVENT: case IBMVNIC_CRQ_XPORT_EVENT:
netif_carrier_off(netdev); netif_carrier_off(netdev);
adapter->crq.active = false; adapter->crq.active = false;
if (adapter->resetting)
adapter->force_reset_recovery = true;
if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) { if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) {
dev_info(dev, "Migrated, re-enabling adapter\n"); dev_info(dev, "Migrated, re-enabling adapter\n");
ibmvnic_reset(adapter, VNIC_RESET_MOBILITY); ibmvnic_reset(adapter, VNIC_RESET_MOBILITY);
......
...@@ -1109,6 +1109,7 @@ struct ibmvnic_adapter { ...@@ -1109,6 +1109,7 @@ struct ibmvnic_adapter {
bool mac_change_pending; bool mac_change_pending;
bool failover_pending; bool failover_pending;
bool force_reset_recovery;
struct ibmvnic_tunables desired; struct ibmvnic_tunables desired;
struct ibmvnic_tunables fallback; struct ibmvnic_tunables fallback;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment