diff options
Diffstat (limited to 'drivers/scsi/libiscsi.c')
| -rw-r--r-- | drivers/scsi/libiscsi.c | 53 |
1 files changed, 49 insertions, 4 deletions
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index c28a712fd4db..703eb6a88790 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
| @@ -1919,10 +1919,11 @@ static int iscsi_has_ping_timed_out(struct iscsi_conn *conn) | |||
| 1919 | static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) | 1919 | static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) |
| 1920 | { | 1920 | { |
| 1921 | enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; | 1921 | enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; |
| 1922 | struct iscsi_task *task = NULL; | 1922 | struct iscsi_task *task = NULL, *running_task; |
| 1923 | struct iscsi_cls_session *cls_session; | 1923 | struct iscsi_cls_session *cls_session; |
| 1924 | struct iscsi_session *session; | 1924 | struct iscsi_session *session; |
| 1925 | struct iscsi_conn *conn; | 1925 | struct iscsi_conn *conn; |
| 1926 | int i; | ||
| 1926 | 1927 | ||
| 1927 | cls_session = starget_to_session(scsi_target(sc->device)); | 1928 | cls_session = starget_to_session(scsi_target(sc->device)); |
| 1928 | session = cls_session->dd_data; | 1929 | session = cls_session->dd_data; |
| @@ -1947,8 +1948,15 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) | |||
| 1947 | } | 1948 | } |
| 1948 | 1949 | ||
| 1949 | task = (struct iscsi_task *)sc->SCp.ptr; | 1950 | task = (struct iscsi_task *)sc->SCp.ptr; |
| 1950 | if (!task) | 1951 | if (!task) { |
| 1952 | /* | ||
| 1953 | * Raced with completion. Just reset timer, and let it | ||
| 1954 | * complete normally | ||
| 1955 | */ | ||
| 1956 | rc = BLK_EH_RESET_TIMER; | ||
| 1951 | goto done; | 1957 | goto done; |
| 1958 | } | ||
| 1959 | |||
| 1952 | /* | 1960 | /* |
| 1953 | * If we have sent (at least queued to the network layer) a pdu or | 1961 | * If we have sent (at least queued to the network layer) a pdu or |
| 1954 | * recvd one for the task since the last timeout ask for | 1962 | * recvd one for the task since the last timeout ask for |
| @@ -1956,10 +1964,10 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) | |||
| 1956 | * we can check if it is the task or connection when we send the | 1964 | * we can check if it is the task or connection when we send the |
| 1957 | * nop as a ping. | 1965 | * nop as a ping. |
| 1958 | */ | 1966 | */ |
| 1959 | if (time_after_eq(task->last_xfer, task->last_timeout)) { | 1967 | if (time_after(task->last_xfer, task->last_timeout)) { |
| 1960 | ISCSI_DBG_EH(session, "Command making progress. Asking " | 1968 | ISCSI_DBG_EH(session, "Command making progress. Asking " |
| 1961 | "scsi-ml for more time to complete. " | 1969 | "scsi-ml for more time to complete. " |
| 1962 | "Last data recv at %lu. Last timeout was at " | 1970 | "Last data xfer at %lu. Last timeout was at " |
| 1963 | "%lu\n.", task->last_xfer, task->last_timeout); | 1971 | "%lu\n.", task->last_xfer, task->last_timeout); |
| 1964 | task->have_checked_conn = false; | 1972 | task->have_checked_conn = false; |
| 1965 | rc = BLK_EH_RESET_TIMER; | 1973 | rc = BLK_EH_RESET_TIMER; |
| @@ -1977,6 +1985,43 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) | |||
| 1977 | goto done; | 1985 | goto done; |
| 1978 | } | 1986 | } |
| 1979 | 1987 | ||
| 1988 | for (i = 0; i < conn->session->cmds_max; i++) { | ||
| 1989 | running_task = conn->session->cmds[i]; | ||
| 1990 | if (!running_task->sc || running_task == task || | ||
| 1991 | running_task->state != ISCSI_TASK_RUNNING) | ||
| 1992 | continue; | ||
| 1993 | |||
| 1994 | /* | ||
| 1995 | * Only check if cmds started before this one have made | ||
| 1996 | * progress, or this could never fail | ||
| 1997 | */ | ||
| 1998 | if (time_after(running_task->sc->jiffies_at_alloc, | ||
| 1999 | task->sc->jiffies_at_alloc)) | ||
| 2000 | continue; | ||
| 2001 | |||
| 2002 | if (time_after(running_task->last_xfer, task->last_timeout)) { | ||
| 2003 | /* | ||
| 2004 | * This task has not made progress, but a task | ||
| 2005 | * started before us has transferred data since | ||
| 2006 | * we started/last-checked. We could be queueing | ||
| 2007 | * too many tasks or the LU is bad. | ||
| 2008 | * | ||
| 2009 | * If the device is bad the cmds ahead of us on | ||
| 2010 | * other devs will complete, and this loop will | ||
| 2011 | * eventually fail starting the scsi eh. | ||
| 2012 | */ | ||
| 2013 | ISCSI_DBG_EH(session, "Command has not made progress " | ||
| 2014 | "but commands ahead of it have. " | ||
| 2015 | "Asking scsi-ml for more time to " | ||
| 2016 | "complete. Our last xfer vs running task " | ||
| 2017 | "last xfer %lu/%lu. Last check %lu.\n", | ||
| 2018 | task->last_xfer, running_task->last_xfer, | ||
| 2019 | task->last_timeout); | ||
| 2020 | rc = BLK_EH_RESET_TIMER; | ||
| 2021 | goto done; | ||
| 2022 | } | ||
| 2023 | } | ||
| 2024 | |||
| 1980 | /* Assumes nop timeout is shorter than scsi cmd timeout */ | 2025 | /* Assumes nop timeout is shorter than scsi cmd timeout */ |
| 1981 | if (task->have_checked_conn) | 2026 | if (task->have_checked_conn) |
| 1982 | goto done; | 2027 | goto done; |
