Commit 73ee5d86 authored by Brian King's avatar Brian King Committed by James Bottomley

[SCSI] ibmvfc: Fix soft lockup on resume

This fixes a softlockup seen on resume. During resume, the CRQ
must be reenabled. However, the H_ENABLE_CRQ hcall used to do
this may return H_BUSY or H_LONG_BUSY. When this happens, the
caller is expected to retry later. Normally the H_ENABLE_CRQ
succeeds relatively soon. However, we have seen cases where
this can take long enough to see softlockup warnings.
This patch changes a simple loop, which was causing the
softlockup, to a loop at task level which sleeps between
retries rather than simply spinning.
Signed-off-by: default avatarBrian King <brking@linux.vnet.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent 15f7fc06
...@@ -504,12 +504,23 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, ...@@ -504,12 +504,23 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS) if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
vhost->action = action; vhost->action = action;
break; break;
case IBMVFC_HOST_ACTION_LOGO:
case IBMVFC_HOST_ACTION_INIT: case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_TGT_DEL: case IBMVFC_HOST_ACTION_TGT_DEL:
switch (vhost->action) {
case IBMVFC_HOST_ACTION_RESET:
case IBMVFC_HOST_ACTION_REENABLE:
break;
default:
vhost->action = action;
break;
};
break;
case IBMVFC_HOST_ACTION_LOGO:
case IBMVFC_HOST_ACTION_QUERY_TGTS: case IBMVFC_HOST_ACTION_QUERY_TGTS:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_NONE: case IBMVFC_HOST_ACTION_NONE:
case IBMVFC_HOST_ACTION_RESET:
case IBMVFC_HOST_ACTION_REENABLE:
default: default:
vhost->action = action; vhost->action = action;
break; break;
...@@ -641,7 +652,7 @@ static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost) ...@@ -641,7 +652,7 @@ static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost)
**/ **/
static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
{ {
long rc; long rc = 0;
struct vio_dev *vdev = to_vio_dev(vhost->dev); struct vio_dev *vdev = to_vio_dev(vhost->dev);
struct ibmvfc_crq_queue *crq = &vhost->crq; struct ibmvfc_crq_queue *crq = &vhost->crq;
...@@ -649,6 +660,8 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) ...@@ -649,6 +660,8 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
free_irq(vdev->irq, vhost); free_irq(vdev->irq, vhost);
tasklet_kill(&vhost->tasklet); tasklet_kill(&vhost->tasklet);
do { do {
if (rc)
msleep(100);
rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
...@@ -667,11 +680,13 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) ...@@ -667,11 +680,13 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
**/ **/
static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
{ {
int rc; int rc = 0;
struct vio_dev *vdev = to_vio_dev(vhost->dev); struct vio_dev *vdev = to_vio_dev(vhost->dev);
/* Re-enable the CRQ */ /* Re-enable the CRQ */
do { do {
if (rc)
msleep(100);
rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
...@@ -690,15 +705,19 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) ...@@ -690,15 +705,19 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
**/ **/
static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
{ {
int rc; int rc = 0;
unsigned long flags;
struct vio_dev *vdev = to_vio_dev(vhost->dev); struct vio_dev *vdev = to_vio_dev(vhost->dev);
struct ibmvfc_crq_queue *crq = &vhost->crq; struct ibmvfc_crq_queue *crq = &vhost->crq;
/* Close the CRQ */ /* Close the CRQ */
do { do {
if (rc)
msleep(100);
rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
spin_lock_irqsave(vhost->host->host_lock, flags);
vhost->state = IBMVFC_NO_CRQ; vhost->state = IBMVFC_NO_CRQ;
vhost->logged_in = 0; vhost->logged_in = 0;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
...@@ -716,6 +735,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) ...@@ -716,6 +735,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
dev_warn(vhost->dev, "Partner adapter not ready\n"); dev_warn(vhost->dev, "Partner adapter not ready\n");
else if (rc != 0) else if (rc != 0)
dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc); dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
return rc; return rc;
} }
...@@ -821,17 +841,9 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code) ...@@ -821,17 +841,9 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
**/ **/
static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost) static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost)
{ {
int rc;
scsi_block_requests(vhost->host);
ibmvfc_purge_requests(vhost, DID_ERROR); ibmvfc_purge_requests(vhost, DID_ERROR);
if ((rc = ibmvfc_reset_crq(vhost)) || ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
(rc = ibmvfc_send_crq_init(vhost)) || ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
(rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
dev_err(vhost->dev, "Error after reset rc=%d\n", rc);
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
} else
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
} }
/** /**
...@@ -2606,22 +2618,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost) ...@@ -2606,22 +2618,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
dev_info(vhost->dev, "Re-enabling adapter\n"); dev_info(vhost->dev, "Re-enabling adapter\n");
vhost->client_migrated = 1; vhost->client_migrated = 1;
ibmvfc_purge_requests(vhost, DID_REQUEUE); ibmvfc_purge_requests(vhost, DID_REQUEUE);
if ((rc = ibmvfc_reenable_crq_queue(vhost)) || ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
(rc = ibmvfc_send_crq_init(vhost))) { ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE);
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
dev_err(vhost->dev, "Error after enable (rc=%ld)\n", rc);
} else
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
} else { } else {
dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format); dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format);
ibmvfc_purge_requests(vhost, DID_ERROR); ibmvfc_purge_requests(vhost, DID_ERROR);
if ((rc = ibmvfc_reset_crq(vhost)) || ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
(rc = ibmvfc_send_crq_init(vhost))) { ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
dev_err(vhost->dev, "Error after reset (rc=%ld)\n", rc);
} else
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
} }
return; return;
case IBMVFC_CRQ_CMD_RSP: case IBMVFC_CRQ_CMD_RSP:
...@@ -4123,6 +4126,8 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) ...@@ -4123,6 +4126,8 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_TGT_DEL: case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_QUERY: case IBMVFC_HOST_ACTION_QUERY:
case IBMVFC_HOST_ACTION_RESET:
case IBMVFC_HOST_ACTION_REENABLE:
default: default:
break; break;
}; };
...@@ -4220,6 +4225,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) ...@@ -4220,6 +4225,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
struct ibmvfc_target *tgt; struct ibmvfc_target *tgt;
unsigned long flags; unsigned long flags;
struct fc_rport *rport; struct fc_rport *rport;
int rc;
ibmvfc_log_ae(vhost, vhost->events_to_log); ibmvfc_log_ae(vhost, vhost->events_to_log);
spin_lock_irqsave(vhost->host->host_lock, flags); spin_lock_irqsave(vhost->host->host_lock, flags);
...@@ -4229,6 +4235,27 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) ...@@ -4229,6 +4235,27 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_LOGO_WAIT: case IBMVFC_HOST_ACTION_LOGO_WAIT:
case IBMVFC_HOST_ACTION_INIT_WAIT: case IBMVFC_HOST_ACTION_INIT_WAIT:
break; break;
case IBMVFC_HOST_ACTION_RESET:
vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
spin_unlock_irqrestore(vhost->host->host_lock, flags);
rc = ibmvfc_reset_crq(vhost);
spin_lock_irqsave(vhost->host->host_lock, flags);
if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
(rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc);
}
break;
case IBMVFC_HOST_ACTION_REENABLE:
vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
spin_unlock_irqrestore(vhost->host->host_lock, flags);
rc = ibmvfc_reenable_crq_queue(vhost);
spin_lock_irqsave(vhost->host->host_lock, flags);
if (rc || (rc = ibmvfc_send_crq_init(vhost))) {
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc);
}
break;
case IBMVFC_HOST_ACTION_LOGO: case IBMVFC_HOST_ACTION_LOGO:
vhost->job_step(vhost); vhost->job_step(vhost);
break; break;
......
...@@ -649,6 +649,8 @@ struct ibmvfc_event_pool { ...@@ -649,6 +649,8 @@ struct ibmvfc_event_pool {
enum ibmvfc_host_action { enum ibmvfc_host_action {
IBMVFC_HOST_ACTION_NONE = 0, IBMVFC_HOST_ACTION_NONE = 0,
IBMVFC_HOST_ACTION_RESET,
IBMVFC_HOST_ACTION_REENABLE,
IBMVFC_HOST_ACTION_LOGO, IBMVFC_HOST_ACTION_LOGO,
IBMVFC_HOST_ACTION_LOGO_WAIT, IBMVFC_HOST_ACTION_LOGO_WAIT,
IBMVFC_HOST_ACTION_INIT, IBMVFC_HOST_ACTION_INIT,
......
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