diff options
author | Brian King <brking@linux.vnet.ibm.com> | 2010-06-17 14:56:00 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-07-27 13:03:46 -0400 |
commit | 0f33ece5bc3d5a9567b65cfbc736e8f206ecfc7b (patch) | |
tree | 4b271d2c1373e1882ecffa8f4baf247a9f578c15 /drivers/scsi/ibmvscsi | |
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')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvscsi.c | 117 | ||||
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvscsi.h | 4 | ||||
-rw-r--r-- | drivers/scsi/ibmvscsi/rpa_vscsi.c | 16 |
3 files changed, 106 insertions, 31 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index aad35cc41e49..e50fad96329c 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c | |||
@@ -73,6 +73,7 @@ | |||
73 | #include <linux/slab.h> | 73 | #include <linux/slab.h> |
74 | #include <linux/of.h> | 74 | #include <linux/of.h> |
75 | #include <linux/pm.h> | 75 | #include <linux/pm.h> |
76 | #include <linux/kthread.h> | ||
76 | #include <asm/firmware.h> | 77 | #include <asm/firmware.h> |
77 | #include <asm/vio.h> | 78 | #include <asm/vio.h> |
78 | #include <scsi/scsi.h> | 79 | #include <scsi/scsi.h> |
@@ -504,14 +505,8 @@ static void ibmvscsi_reset_host(struct ibmvscsi_host_data *hostdata) | |||
504 | atomic_set(&hostdata->request_limit, 0); | 505 | atomic_set(&hostdata->request_limit, 0); |
505 | 506 | ||
506 | purge_requests(hostdata, DID_ERROR); | 507 | purge_requests(hostdata, DID_ERROR); |
507 | if ((ibmvscsi_ops->reset_crq_queue(&hostdata->queue, hostdata)) || | 508 | hostdata->reset_crq = 1; |
508 | (ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0)) || | 509 | wake_up(&hostdata->work_wait_q); |
509 | (vio_enable_interrupts(to_vio_dev(hostdata->dev)))) { | ||
510 | atomic_set(&hostdata->request_limit, -1); | ||
511 | dev_err(hostdata->dev, "error after reset\n"); | ||
512 | } | ||
513 | |||
514 | scsi_unblock_requests(hostdata->host); | ||
515 | } | 510 | } |
516 | 511 | ||
517 | /** | 512 | /** |
@@ -1462,30 +1457,14 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq, | |||
1462 | /* We need to re-setup the interpartition connection */ | 1457 | /* We need to re-setup the interpartition connection */ |
1463 | dev_info(hostdata->dev, "Re-enabling adapter!\n"); | 1458 | dev_info(hostdata->dev, "Re-enabling adapter!\n"); |
1464 | hostdata->client_migrated = 1; | 1459 | hostdata->client_migrated = 1; |
1460 | hostdata->reenable_crq = 1; | ||
1465 | purge_requests(hostdata, DID_REQUEUE); | 1461 | purge_requests(hostdata, DID_REQUEUE); |
1466 | if ((ibmvscsi_ops->reenable_crq_queue(&hostdata->queue, | 1462 | wake_up(&hostdata->work_wait_q); |
1467 | hostdata)) || | ||
1468 | (ibmvscsi_ops->send_crq(hostdata, | ||
1469 | 0xC001000000000000LL, 0))) { | ||
1470 | atomic_set(&hostdata->request_limit, | ||
1471 | -1); | ||
1472 | dev_err(hostdata->dev, "error after enable\n"); | ||
1473 | } | ||
1474 | } else { | 1463 | } else { |
1475 | dev_err(hostdata->dev, "Virtual adapter failed rc %d!\n", | 1464 | dev_err(hostdata->dev, "Virtual adapter failed rc %d!\n", |
1476 | crq->format); | 1465 | crq->format); |
1477 | 1466 | ibmvscsi_reset_host(hostdata); | |
1478 | purge_requests(hostdata, DID_ERROR); | ||
1479 | if ((ibmvscsi_ops->reset_crq_queue(&hostdata->queue, | ||
1480 | hostdata)) || | ||
1481 | (ibmvscsi_ops->send_crq(hostdata, | ||
1482 | 0xC001000000000000LL, 0))) { | ||
1483 | atomic_set(&hostdata->request_limit, | ||
1484 | -1); | ||
1485 | dev_err(hostdata->dev, "error after reset\n"); | ||
1486 | } | ||
1487 | } | 1467 | } |
1488 | scsi_unblock_requests(hostdata->host); | ||
1489 | return; | 1468 | return; |
1490 | case 0x80: /* real payload */ | 1469 | case 0x80: /* real payload */ |
1491 | break; | 1470 | break; |
@@ -1850,6 +1829,75 @@ static unsigned long ibmvscsi_get_desired_dma(struct vio_dev *vdev) | |||
1850 | return desired_io; | 1829 | return desired_io; |
1851 | } | 1830 | } |
1852 | 1831 | ||
1832 | static void ibmvscsi_do_work(struct ibmvscsi_host_data *hostdata) | ||
1833 | { | ||
1834 | int rc; | ||
1835 | char *action = "reset"; | ||
1836 | |||
1837 | if (hostdata->reset_crq) { | ||
1838 | smp_rmb(); | ||
1839 | hostdata->reset_crq = 0; | ||
1840 | |||
1841 | rc = ibmvscsi_ops->reset_crq_queue(&hostdata->queue, hostdata); | ||
1842 | if (!rc) | ||
1843 | rc = ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0); | ||
1844 | if (!rc) | ||
1845 | rc = vio_enable_interrupts(to_vio_dev(hostdata->dev)); | ||
1846 | } else if (hostdata->reenable_crq) { | ||
1847 | smp_rmb(); | ||
1848 | action = "enable"; | ||
1849 | rc = ibmvscsi_ops->reenable_crq_queue(&hostdata->queue, hostdata); | ||
1850 | hostdata->reenable_crq = 0; | ||
1851 | if (!rc) | ||
1852 | rc = ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0); | ||
1853 | } else | ||
1854 | return; | ||
1855 | |||
1856 | if (rc) { | ||
1857 | atomic_set(&hostdata->request_limit, -1); | ||
1858 | dev_err(hostdata->dev, "error after %s\n", action); | ||
1859 | } | ||
1860 | |||
1861 | scsi_unblock_requests(hostdata->host); | ||
1862 | } | ||
1863 | |||
1864 | static int ibmvscsi_work_to_do(struct ibmvscsi_host_data *hostdata) | ||
1865 | { | ||
1866 | if (kthread_should_stop()) | ||
1867 | return 1; | ||
1868 | else if (hostdata->reset_crq) { | ||
1869 | smp_rmb(); | ||
1870 | return 1; | ||
1871 | } else if (hostdata->reenable_crq) { | ||
1872 | smp_rmb(); | ||
1873 | return 1; | ||
1874 | } | ||
1875 | |||
1876 | return 0; | ||
1877 | } | ||
1878 | |||
1879 | static int ibmvscsi_work(void *data) | ||
1880 | { | ||
1881 | struct ibmvscsi_host_data *hostdata = data; | ||
1882 | int rc; | ||
1883 | |||
1884 | set_user_nice(current, -20); | ||
1885 | |||
1886 | while (1) { | ||
1887 | rc = wait_event_interruptible(hostdata->work_wait_q, | ||
1888 | ibmvscsi_work_to_do(hostdata)); | ||
1889 | |||
1890 | BUG_ON(rc); | ||
1891 | |||
1892 | if (kthread_should_stop()) | ||
1893 | break; | ||
1894 | |||
1895 | ibmvscsi_do_work(hostdata); | ||
1896 | } | ||
1897 | |||
1898 | return 0; | ||
1899 | } | ||
1900 | |||
1853 | /** | 1901 | /** |
1854 | * Called by bus code for each adapter | 1902 | * Called by bus code for each adapter |
1855 | */ | 1903 | */ |
@@ -1875,6 +1923,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
1875 | hostdata = shost_priv(host); | 1923 | hostdata = shost_priv(host); |
1876 | memset(hostdata, 0x00, sizeof(*hostdata)); | 1924 | memset(hostdata, 0x00, sizeof(*hostdata)); |
1877 | INIT_LIST_HEAD(&hostdata->sent); | 1925 | INIT_LIST_HEAD(&hostdata->sent); |
1926 | init_waitqueue_head(&hostdata->work_wait_q); | ||
1878 | hostdata->host = host; | 1927 | hostdata->host = host; |
1879 | hostdata->dev = dev; | 1928 | hostdata->dev = dev; |
1880 | atomic_set(&hostdata->request_limit, -1); | 1929 | atomic_set(&hostdata->request_limit, -1); |
@@ -1885,10 +1934,19 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
1885 | goto persist_bufs_failed; | 1934 | goto persist_bufs_failed; |
1886 | } | 1935 | } |
1887 | 1936 | ||
1937 | hostdata->work_thread = kthread_run(ibmvscsi_work, hostdata, "%s_%d", | ||
1938 | "ibmvscsi", host->host_no); | ||
1939 | |||
1940 | if (IS_ERR(hostdata->work_thread)) { | ||
1941 | dev_err(&vdev->dev, "couldn't initialize kthread. rc=%ld\n", | ||
1942 | PTR_ERR(hostdata->work_thread)); | ||
1943 | goto init_crq_failed; | ||
1944 | } | ||
1945 | |||
1888 | rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_events); | 1946 | rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_events); |
1889 | if (rc != 0 && rc != H_RESOURCE) { | 1947 | if (rc != 0 && rc != H_RESOURCE) { |
1890 | dev_err(&vdev->dev, "couldn't initialize crq. rc=%d\n", rc); | 1948 | dev_err(&vdev->dev, "couldn't initialize crq. rc=%d\n", rc); |
1891 | goto init_crq_failed; | 1949 | goto kill_kthread; |
1892 | } | 1950 | } |
1893 | if (initialize_event_pool(&hostdata->pool, max_events, hostdata) != 0) { | 1951 | if (initialize_event_pool(&hostdata->pool, max_events, hostdata) != 0) { |
1894 | dev_err(&vdev->dev, "couldn't initialize event pool\n"); | 1952 | dev_err(&vdev->dev, "couldn't initialize event pool\n"); |
@@ -1944,6 +2002,8 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
1944 | release_event_pool(&hostdata->pool, hostdata); | 2002 | release_event_pool(&hostdata->pool, hostdata); |
1945 | init_pool_failed: | 2003 | init_pool_failed: |
1946 | ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_events); | 2004 | ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_events); |
2005 | kill_kthread: | ||
2006 | kthread_stop(hostdata->work_thread); | ||
1947 | init_crq_failed: | 2007 | init_crq_failed: |
1948 | unmap_persist_bufs(hostdata); | 2008 | unmap_persist_bufs(hostdata); |
1949 | persist_bufs_failed: | 2009 | persist_bufs_failed: |
@@ -1960,6 +2020,7 @@ static int ibmvscsi_remove(struct vio_dev *vdev) | |||
1960 | ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, | 2020 | ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, |
1961 | max_events); | 2021 | max_events); |
1962 | 2022 | ||
2023 | kthread_stop(hostdata->work_thread); | ||
1963 | srp_remove_host(hostdata->host); | 2024 | srp_remove_host(hostdata->host); |
1964 | scsi_remove_host(hostdata->host); | 2025 | scsi_remove_host(hostdata->host); |
1965 | scsi_host_put(hostdata->host); | 2026 | scsi_host_put(hostdata->host); |
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h index 9cb7c6a773e1..02197a2b22b9 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.h +++ b/drivers/scsi/ibmvscsi/ibmvscsi.h | |||
@@ -91,12 +91,16 @@ struct event_pool { | |||
91 | struct ibmvscsi_host_data { | 91 | struct ibmvscsi_host_data { |
92 | atomic_t request_limit; | 92 | atomic_t request_limit; |
93 | int client_migrated; | 93 | int client_migrated; |
94 | int reset_crq; | ||
95 | int reenable_crq; | ||
94 | struct device *dev; | 96 | struct device *dev; |
95 | struct event_pool pool; | 97 | struct event_pool pool; |
96 | struct crq_queue queue; | 98 | struct crq_queue queue; |
97 | struct tasklet_struct srp_task; | 99 | struct tasklet_struct srp_task; |
98 | struct list_head sent; | 100 | struct list_head sent; |
99 | struct Scsi_Host *host; | 101 | struct Scsi_Host *host; |
102 | struct task_struct *work_thread; | ||
103 | wait_queue_head_t work_wait_q; | ||
100 | struct mad_adapter_info_data madapter_info; | 104 | struct mad_adapter_info_data madapter_info; |
101 | struct capabilities caps; | 105 | struct capabilities caps; |
102 | dma_addr_t caps_addr; | 106 | dma_addr_t caps_addr; |
diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 989b9a8ba72d..f48ae0190d95 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <asm/prom.h> | 31 | #include <asm/prom.h> |
32 | #include <asm/iommu.h> | 32 | #include <asm/iommu.h> |
33 | #include <asm/hvcall.h> | 33 | #include <asm/hvcall.h> |
34 | #include <linux/delay.h> | ||
34 | #include <linux/dma-mapping.h> | 35 | #include <linux/dma-mapping.h> |
35 | #include <linux/gfp.h> | 36 | #include <linux/gfp.h> |
36 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
@@ -71,11 +72,13 @@ static void rpavscsi_release_crq_queue(struct crq_queue *queue, | |||
71 | struct ibmvscsi_host_data *hostdata, | 72 | struct ibmvscsi_host_data *hostdata, |
72 | int max_requests) | 73 | int max_requests) |
73 | { | 74 | { |
74 | long rc; | 75 | long rc = 0; |
75 | struct vio_dev *vdev = to_vio_dev(hostdata->dev); | 76 | struct vio_dev *vdev = to_vio_dev(hostdata->dev); |
76 | free_irq(vdev->irq, (void *)hostdata); | 77 | free_irq(vdev->irq, (void *)hostdata); |
77 | tasklet_kill(&hostdata->srp_task); | 78 | tasklet_kill(&hostdata->srp_task); |
78 | do { | 79 | do { |
80 | if (rc) | ||
81 | msleep(100); | ||
79 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 82 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
80 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); | 83 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); |
81 | dma_unmap_single(hostdata->dev, | 84 | dma_unmap_single(hostdata->dev, |
@@ -200,11 +203,13 @@ static void set_adapter_info(struct ibmvscsi_host_data *hostdata) | |||
200 | static int rpavscsi_reset_crq_queue(struct crq_queue *queue, | 203 | static int rpavscsi_reset_crq_queue(struct crq_queue *queue, |
201 | struct ibmvscsi_host_data *hostdata) | 204 | struct ibmvscsi_host_data *hostdata) |
202 | { | 205 | { |
203 | int rc; | 206 | int rc = 0; |
204 | struct vio_dev *vdev = to_vio_dev(hostdata->dev); | 207 | struct vio_dev *vdev = to_vio_dev(hostdata->dev); |
205 | 208 | ||
206 | /* Close the CRQ */ | 209 | /* Close the CRQ */ |
207 | do { | 210 | do { |
211 | if (rc) | ||
212 | msleep(100); | ||
208 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 213 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
209 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); | 214 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); |
210 | 215 | ||
@@ -301,7 +306,10 @@ static int rpavscsi_init_crq_queue(struct crq_queue *queue, | |||
301 | 306 | ||
302 | req_irq_failed: | 307 | req_irq_failed: |
303 | tasklet_kill(&hostdata->srp_task); | 308 | tasklet_kill(&hostdata->srp_task); |
309 | rc = 0; | ||
304 | do { | 310 | do { |
311 | if (rc) | ||
312 | msleep(100); | ||
305 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 313 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
306 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); | 314 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); |
307 | reg_crq_failed: | 315 | reg_crq_failed: |
@@ -323,11 +331,13 @@ static int rpavscsi_init_crq_queue(struct crq_queue *queue, | |||
323 | static int rpavscsi_reenable_crq_queue(struct crq_queue *queue, | 331 | static int rpavscsi_reenable_crq_queue(struct crq_queue *queue, |
324 | struct ibmvscsi_host_data *hostdata) | 332 | struct ibmvscsi_host_data *hostdata) |
325 | { | 333 | { |
326 | int rc; | 334 | int rc = 0; |
327 | struct vio_dev *vdev = to_vio_dev(hostdata->dev); | 335 | struct vio_dev *vdev = to_vio_dev(hostdata->dev); |
328 | 336 | ||
329 | /* Re-enable the CRQ */ | 337 | /* Re-enable the CRQ */ |
330 | do { | 338 | do { |
339 | if (rc) | ||
340 | msleep(100); | ||
331 | rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); | 341 | rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); |
332 | } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); | 342 | } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); |
333 | 343 | ||