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/ibmvscsi.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/ibmvscsi.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvscsi.c | 117 |
1 files changed, 89 insertions, 28 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); |