diff options
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 207 |
1 files changed, 147 insertions, 60 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index ae65fa60eeb4..af69c0738edb 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c | |||
@@ -758,6 +758,9 @@ static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt) | |||
758 | cmnd->scsi_done(cmnd); | 758 | cmnd->scsi_done(cmnd); |
759 | } | 759 | } |
760 | 760 | ||
761 | if (evt->eh_comp) | ||
762 | complete(evt->eh_comp); | ||
763 | |||
761 | ibmvfc_free_event(evt); | 764 | ibmvfc_free_event(evt); |
762 | } | 765 | } |
763 | 766 | ||
@@ -1245,6 +1248,7 @@ static void ibmvfc_init_event(struct ibmvfc_event *evt, | |||
1245 | evt->sync_iu = NULL; | 1248 | evt->sync_iu = NULL; |
1246 | evt->crq.format = format; | 1249 | evt->crq.format = format; |
1247 | evt->done = done; | 1250 | evt->done = done; |
1251 | evt->eh_comp = NULL; | ||
1248 | } | 1252 | } |
1249 | 1253 | ||
1250 | /** | 1254 | /** |
@@ -1485,6 +1489,9 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt) | |||
1485 | cmnd->scsi_done(cmnd); | 1489 | cmnd->scsi_done(cmnd); |
1486 | } | 1490 | } |
1487 | 1491 | ||
1492 | if (evt->eh_comp) | ||
1493 | complete(evt->eh_comp); | ||
1494 | |||
1488 | ibmvfc_free_event(evt); | 1495 | ibmvfc_free_event(evt); |
1489 | } | 1496 | } |
1490 | 1497 | ||
@@ -1785,7 +1792,8 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev) | |||
1785 | static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) | 1792 | static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) |
1786 | { | 1793 | { |
1787 | struct ibmvfc_host *vhost = shost_priv(sdev->host); | 1794 | struct ibmvfc_host *vhost = shost_priv(sdev->host); |
1788 | struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); | 1795 | struct scsi_target *starget = scsi_target(sdev); |
1796 | struct fc_rport *rport = starget_to_rport(starget); | ||
1789 | struct ibmvfc_tmf *tmf; | 1797 | struct ibmvfc_tmf *tmf; |
1790 | struct ibmvfc_event *evt, *found_evt; | 1798 | struct ibmvfc_event *evt, *found_evt; |
1791 | union ibmvfc_iu rsp; | 1799 | union ibmvfc_iu rsp; |
@@ -1823,7 +1831,7 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) | |||
1823 | int_to_scsilun(sdev->lun, &tmf->lun); | 1831 | int_to_scsilun(sdev->lun, &tmf->lun); |
1824 | tmf->flags = (type | IBMVFC_TMF_LUA_VALID); | 1832 | tmf->flags = (type | IBMVFC_TMF_LUA_VALID); |
1825 | tmf->cancel_key = (unsigned long)sdev->hostdata; | 1833 | tmf->cancel_key = (unsigned long)sdev->hostdata; |
1826 | tmf->my_cancel_key = (IBMVFC_TMF_CANCEL_KEY | (unsigned long)sdev->hostdata); | 1834 | tmf->my_cancel_key = (unsigned long)starget->hostdata; |
1827 | 1835 | ||
1828 | evt->sync_iu = &rsp; | 1836 | evt->sync_iu = &rsp; |
1829 | init_completion(&evt->comp); | 1837 | init_completion(&evt->comp); |
@@ -1855,6 +1863,91 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) | |||
1855 | } | 1863 | } |
1856 | 1864 | ||
1857 | /** | 1865 | /** |
1866 | * ibmvfc_match_target - Match function for specified target | ||
1867 | * @evt: ibmvfc event struct | ||
1868 | * @device: device to match (starget) | ||
1869 | * | ||
1870 | * Returns: | ||
1871 | * 1 if event matches starget / 0 if event does not match starget | ||
1872 | **/ | ||
1873 | static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device) | ||
1874 | { | ||
1875 | if (evt->cmnd && scsi_target(evt->cmnd->device) == device) | ||
1876 | return 1; | ||
1877 | return 0; | ||
1878 | } | ||
1879 | |||
1880 | /** | ||
1881 | * ibmvfc_match_lun - Match function for specified LUN | ||
1882 | * @evt: ibmvfc event struct | ||
1883 | * @device: device to match (sdev) | ||
1884 | * | ||
1885 | * Returns: | ||
1886 | * 1 if event matches sdev / 0 if event does not match sdev | ||
1887 | **/ | ||
1888 | static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device) | ||
1889 | { | ||
1890 | if (evt->cmnd && evt->cmnd->device == device) | ||
1891 | return 1; | ||
1892 | return 0; | ||
1893 | } | ||
1894 | |||
1895 | /** | ||
1896 | * ibmvfc_wait_for_ops - Wait for ops to complete | ||
1897 | * @vhost: ibmvfc host struct | ||
1898 | * @device: device to match (starget or sdev) | ||
1899 | * @match: match function | ||
1900 | * | ||
1901 | * Returns: | ||
1902 | * SUCCESS / FAILED | ||
1903 | **/ | ||
1904 | static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, | ||
1905 | int (*match) (struct ibmvfc_event *, void *)) | ||
1906 | { | ||
1907 | struct ibmvfc_event *evt; | ||
1908 | DECLARE_COMPLETION_ONSTACK(comp); | ||
1909 | int wait; | ||
1910 | unsigned long flags; | ||
1911 | signed long timeout = init_timeout * HZ; | ||
1912 | |||
1913 | ENTER; | ||
1914 | do { | ||
1915 | wait = 0; | ||
1916 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
1917 | list_for_each_entry(evt, &vhost->sent, queue) { | ||
1918 | if (match(evt, device)) { | ||
1919 | evt->eh_comp = ∁ | ||
1920 | wait++; | ||
1921 | } | ||
1922 | } | ||
1923 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1924 | |||
1925 | if (wait) { | ||
1926 | timeout = wait_for_completion_timeout(&comp, timeout); | ||
1927 | |||
1928 | if (!timeout) { | ||
1929 | wait = 0; | ||
1930 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
1931 | list_for_each_entry(evt, &vhost->sent, queue) { | ||
1932 | if (match(evt, device)) { | ||
1933 | evt->eh_comp = NULL; | ||
1934 | wait++; | ||
1935 | } | ||
1936 | } | ||
1937 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1938 | if (wait) | ||
1939 | dev_err(vhost->dev, "Timed out waiting for aborted commands\n"); | ||
1940 | LEAVE; | ||
1941 | return wait ? FAILED : SUCCESS; | ||
1942 | } | ||
1943 | } | ||
1944 | } while (wait); | ||
1945 | |||
1946 | LEAVE; | ||
1947 | return SUCCESS; | ||
1948 | } | ||
1949 | |||
1950 | /** | ||
1858 | * ibmvfc_eh_abort_handler - Abort a command | 1951 | * ibmvfc_eh_abort_handler - Abort a command |
1859 | * @cmd: scsi command to abort | 1952 | * @cmd: scsi command to abort |
1860 | * | 1953 | * |
@@ -1863,29 +1956,21 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) | |||
1863 | **/ | 1956 | **/ |
1864 | static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd) | 1957 | static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd) |
1865 | { | 1958 | { |
1866 | struct ibmvfc_host *vhost = shost_priv(cmd->device->host); | 1959 | struct scsi_device *sdev = cmd->device; |
1867 | struct ibmvfc_event *evt, *pos; | 1960 | struct ibmvfc_host *vhost = shost_priv(sdev->host); |
1868 | int cancel_rc, abort_rc; | 1961 | int cancel_rc, abort_rc; |
1869 | unsigned long flags; | 1962 | int rc = FAILED; |
1870 | 1963 | ||
1871 | ENTER; | 1964 | ENTER; |
1872 | ibmvfc_wait_while_resetting(vhost); | 1965 | ibmvfc_wait_while_resetting(vhost); |
1873 | cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_ABORT_TASK_SET); | 1966 | cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET); |
1874 | abort_rc = ibmvfc_abort_task_set(cmd->device); | 1967 | abort_rc = ibmvfc_abort_task_set(sdev); |
1875 | 1968 | ||
1876 | if (!cancel_rc && !abort_rc) { | 1969 | if (!cancel_rc && !abort_rc) |
1877 | spin_lock_irqsave(vhost->host->host_lock, flags); | 1970 | rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun); |
1878 | list_for_each_entry_safe(evt, pos, &vhost->sent, queue) { | ||
1879 | if (evt->cmnd && evt->cmnd->device == cmd->device) | ||
1880 | ibmvfc_fail_request(evt, DID_ABORT); | ||
1881 | } | ||
1882 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1883 | LEAVE; | ||
1884 | return SUCCESS; | ||
1885 | } | ||
1886 | 1971 | ||
1887 | LEAVE; | 1972 | LEAVE; |
1888 | return FAILED; | 1973 | return rc; |
1889 | } | 1974 | } |
1890 | 1975 | ||
1891 | /** | 1976 | /** |
@@ -1897,29 +1982,21 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd) | |||
1897 | **/ | 1982 | **/ |
1898 | static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd) | 1983 | static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd) |
1899 | { | 1984 | { |
1900 | struct ibmvfc_host *vhost = shost_priv(cmd->device->host); | 1985 | struct scsi_device *sdev = cmd->device; |
1901 | struct ibmvfc_event *evt, *pos; | 1986 | struct ibmvfc_host *vhost = shost_priv(sdev->host); |
1902 | int cancel_rc, reset_rc; | 1987 | int cancel_rc, reset_rc; |
1903 | unsigned long flags; | 1988 | int rc = FAILED; |
1904 | 1989 | ||
1905 | ENTER; | 1990 | ENTER; |
1906 | ibmvfc_wait_while_resetting(vhost); | 1991 | ibmvfc_wait_while_resetting(vhost); |
1907 | cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_LUN_RESET); | 1992 | cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET); |
1908 | reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_LUN_RESET, "LUN"); | 1993 | reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN"); |
1909 | 1994 | ||
1910 | if (!cancel_rc && !reset_rc) { | 1995 | if (!cancel_rc && !reset_rc) |
1911 | spin_lock_irqsave(vhost->host->host_lock, flags); | 1996 | rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun); |
1912 | list_for_each_entry_safe(evt, pos, &vhost->sent, queue) { | ||
1913 | if (evt->cmnd && evt->cmnd->device == cmd->device) | ||
1914 | ibmvfc_fail_request(evt, DID_ABORT); | ||
1915 | } | ||
1916 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1917 | LEAVE; | ||
1918 | return SUCCESS; | ||
1919 | } | ||
1920 | 1997 | ||
1921 | LEAVE; | 1998 | LEAVE; |
1922 | return FAILED; | 1999 | return rc; |
1923 | } | 2000 | } |
1924 | 2001 | ||
1925 | /** | 2002 | /** |
@@ -1955,31 +2032,23 @@ static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data) | |||
1955 | **/ | 2032 | **/ |
1956 | static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd) | 2033 | static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd) |
1957 | { | 2034 | { |
1958 | struct ibmvfc_host *vhost = shost_priv(cmd->device->host); | 2035 | struct scsi_device *sdev = cmd->device; |
1959 | struct scsi_target *starget = scsi_target(cmd->device); | 2036 | struct ibmvfc_host *vhost = shost_priv(sdev->host); |
1960 | struct ibmvfc_event *evt, *pos; | 2037 | struct scsi_target *starget = scsi_target(sdev); |
1961 | int reset_rc; | 2038 | int reset_rc; |
2039 | int rc = FAILED; | ||
1962 | unsigned long cancel_rc = 0; | 2040 | unsigned long cancel_rc = 0; |
1963 | unsigned long flags; | ||
1964 | 2041 | ||
1965 | ENTER; | 2042 | ENTER; |
1966 | ibmvfc_wait_while_resetting(vhost); | 2043 | ibmvfc_wait_while_resetting(vhost); |
1967 | starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); | 2044 | starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); |
1968 | reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_TARGET_RESET, "target"); | 2045 | reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target"); |
1969 | 2046 | ||
1970 | if (!cancel_rc && !reset_rc) { | 2047 | if (!cancel_rc && !reset_rc) |
1971 | spin_lock_irqsave(vhost->host->host_lock, flags); | 2048 | rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target); |
1972 | list_for_each_entry_safe(evt, pos, &vhost->sent, queue) { | ||
1973 | if (evt->cmnd && scsi_target(evt->cmnd->device) == starget) | ||
1974 | ibmvfc_fail_request(evt, DID_ABORT); | ||
1975 | } | ||
1976 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1977 | LEAVE; | ||
1978 | return SUCCESS; | ||
1979 | } | ||
1980 | 2049 | ||
1981 | LEAVE; | 2050 | LEAVE; |
1982 | return FAILED; | 2051 | return rc; |
1983 | } | 2052 | } |
1984 | 2053 | ||
1985 | /** | 2054 | /** |
@@ -2009,23 +2078,18 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport) | |||
2009 | struct scsi_target *starget = to_scsi_target(&rport->dev); | 2078 | struct scsi_target *starget = to_scsi_target(&rport->dev); |
2010 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | 2079 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); |
2011 | struct ibmvfc_host *vhost = shost_priv(shost); | 2080 | struct ibmvfc_host *vhost = shost_priv(shost); |
2012 | struct ibmvfc_event *evt, *pos; | ||
2013 | unsigned long cancel_rc = 0; | 2081 | unsigned long cancel_rc = 0; |
2014 | unsigned long abort_rc = 0; | 2082 | unsigned long abort_rc = 0; |
2015 | unsigned long flags; | 2083 | int rc = FAILED; |
2016 | 2084 | ||
2017 | ENTER; | 2085 | ENTER; |
2018 | starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); | 2086 | starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); |
2019 | starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all); | 2087 | starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all); |
2020 | 2088 | ||
2021 | if (!cancel_rc && !abort_rc) { | 2089 | if (!cancel_rc && !abort_rc) |
2022 | spin_lock_irqsave(shost->host_lock, flags); | 2090 | rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target); |
2023 | list_for_each_entry_safe(evt, pos, &vhost->sent, queue) { | 2091 | |
2024 | if (evt->cmnd && scsi_target(evt->cmnd->device) == starget) | 2092 | if (rc == FAILED) |
2025 | ibmvfc_fail_request(evt, DID_ABORT); | ||
2026 | } | ||
2027 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
2028 | } else | ||
2029 | ibmvfc_issue_fc_host_lip(shost); | 2093 | ibmvfc_issue_fc_host_lip(shost); |
2030 | LEAVE; | 2094 | LEAVE; |
2031 | } | 2095 | } |
@@ -2259,6 +2323,28 @@ static int ibmvfc_slave_alloc(struct scsi_device *sdev) | |||
2259 | } | 2323 | } |
2260 | 2324 | ||
2261 | /** | 2325 | /** |
2326 | * ibmvfc_target_alloc - Setup the target's task set value | ||
2327 | * @starget: struct scsi_target | ||
2328 | * | ||
2329 | * Set the target's task set value so that error handling works as | ||
2330 | * expected. | ||
2331 | * | ||
2332 | * Returns: | ||
2333 | * 0 on success / -ENXIO if device does not exist | ||
2334 | **/ | ||
2335 | static int ibmvfc_target_alloc(struct scsi_target *starget) | ||
2336 | { | ||
2337 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||
2338 | struct ibmvfc_host *vhost = shost_priv(shost); | ||
2339 | unsigned long flags = 0; | ||
2340 | |||
2341 | spin_lock_irqsave(shost->host_lock, flags); | ||
2342 | starget->hostdata = (void *)(unsigned long)vhost->task_set++; | ||
2343 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
2344 | return 0; | ||
2345 | } | ||
2346 | |||
2347 | /** | ||
2262 | * ibmvfc_slave_configure - Configure the device | 2348 | * ibmvfc_slave_configure - Configure the device |
2263 | * @sdev: struct scsi_device device to configure | 2349 | * @sdev: struct scsi_device device to configure |
2264 | * | 2350 | * |
@@ -2537,6 +2623,7 @@ static struct scsi_host_template driver_template = { | |||
2537 | .eh_host_reset_handler = ibmvfc_eh_host_reset_handler, | 2623 | .eh_host_reset_handler = ibmvfc_eh_host_reset_handler, |
2538 | .slave_alloc = ibmvfc_slave_alloc, | 2624 | .slave_alloc = ibmvfc_slave_alloc, |
2539 | .slave_configure = ibmvfc_slave_configure, | 2625 | .slave_configure = ibmvfc_slave_configure, |
2626 | .target_alloc = ibmvfc_target_alloc, | ||
2540 | .scan_finished = ibmvfc_scan_finished, | 2627 | .scan_finished = ibmvfc_scan_finished, |
2541 | .change_queue_depth = ibmvfc_change_queue_depth, | 2628 | .change_queue_depth = ibmvfc_change_queue_depth, |
2542 | .change_queue_type = ibmvfc_change_queue_type, | 2629 | .change_queue_type = ibmvfc_change_queue_type, |