diff options
author | Brian King <brking@linux.vnet.ibm.com> | 2010-06-17 13:56:00 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-07-27 12:03:46 -0500 |
commit | 0f33ece5bc3d5a9567b65cfbc736e8f206ecfc7b (patch) | |
tree | 4b271d2c1373e1882ecffa8f4baf247a9f578c15 /drivers/scsi/ibmvscsi/rpa_vscsi.c | |
parent | 06395193b20124663b83b2894da827aec7e9d920 (diff) |
[SCSI] ibmvscsi: Fix softlockup 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. 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: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/ibmvscsi/rpa_vscsi.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/rpa_vscsi.c | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 989b9a8ba72..f48ae0190d9 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -31,6 +31,7 @@ #include <asm/prom.h> #include <asm/iommu.h> #include <asm/hvcall.h> +#include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/gfp.h> #include <linux/interrupt.h> @@ -71,11 +72,13 @@ static void rpavscsi_release_crq_queue(struct crq_queue *queue, struct ibmvscsi_host_data *hostdata, int max_requests) { - long rc; + long rc = 0; struct vio_dev *vdev = to_vio_dev(hostdata->dev); free_irq(vdev->irq, (void *)hostdata); tasklet_kill(&hostdata->srp_task); do { + if (rc) + msleep(100); rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); dma_unmap_single(hostdata->dev, @@ -200,11 +203,13 @@ static void set_adapter_info(struct ibmvscsi_host_data *hostdata) static int rpavscsi_reset_crq_queue(struct crq_queue *queue, struct ibmvscsi_host_data *hostdata) { - int rc; + int rc = 0; struct vio_dev *vdev = to_vio_dev(hostdata->dev); /* Close the CRQ */ do { + if (rc) + msleep(100); rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); @@ -301,7 +306,10 @@ static int rpavscsi_init_crq_queue(struct crq_queue *queue, req_irq_failed: tasklet_kill(&hostdata->srp_task); + rc = 0; do { + if (rc) + msleep(100); rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); reg_crq_failed: @@ -323,11 +331,13 @@ static int rpavscsi_init_crq_queue(struct crq_queue *queue, static int rpavscsi_reenable_crq_queue(struct crq_queue *queue, struct ibmvscsi_host_data *hostdata) { - int rc; + int rc = 0; struct vio_dev *vdev = to_vio_dev(hostdata->dev); /* Re-enable the CRQ */ do { + if (rc) + msleep(100); rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); |