The next patches remove the session->cmds array for the scsi_cmnd iscsi tasks. This patch has us use scsi_host_busy_iter instead of looping over that array for the scsi_cmnd case, so we can remove it in the next patches when we also switch over to using the blk layer cmd allocators. Signed-off-by: Mike Christie <michael.christie@xxxxxxxxxx> --- drivers/scsi/libiscsi.c | 160 ++++++++++++++++++++++++---------------- include/scsi/libiscsi.h | 12 +++ 2 files changed, 110 insertions(+), 62 deletions(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 07b23f3967a9..8a9a9f5801e3 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1909,41 +1909,69 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, return 0; } -/* - * Fail commands. session frwd lock held and xmit thread flushed. - */ -static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error) +static bool fail_scsi_task_iter(struct scsi_cmnd *sc, void *data, bool rsvd) { + struct iscsi_task *task = (struct iscsi_task *)sc->SCp.ptr; + struct iscsi_sc_iter_data *iter_data = data; + struct iscsi_conn *conn = iter_data->conn; struct iscsi_session *session = conn->session; - struct iscsi_task *task; - int i; + + ISCSI_DBG_SESSION(session, "failing sc %p itt 0x%x state %d\n", + task->sc, task->itt, task->state); + __iscsi_get_task(task); + spin_unlock_bh(&session->back_lock); + + fail_scsi_task(task, *(int *)iter_data->data); + + spin_unlock_bh(&session->frwd_lock); + iscsi_put_task(task); + spin_lock_bh(&session->frwd_lock); spin_lock_bh(&session->back_lock); - for (i = 0; i < session->cmds_max; i++) { - task = session->cmds[i]; - if (!task->sc || task->state == ISCSI_TASK_FREE) - continue; + return true; +} - if (lun != -1 && lun != task->sc->device->lun) - continue; +static bool iscsi_sc_iter(struct scsi_cmnd *sc, void *data, bool rsvd) +{ + struct iscsi_task *task = (struct iscsi_task *)sc->SCp.ptr; + struct iscsi_sc_iter_data *iter_data = data; - __iscsi_get_task(task); - spin_unlock_bh(&session->back_lock); + if (!task || !task->sc || task->state == ISCSI_TASK_FREE || + task->conn != iter_data->conn) + return true; - ISCSI_DBG_SESSION(session, - "failing sc %p itt 0x%x state %d\n", - task->sc, task->itt, task->state); - fail_scsi_task(task, error); + if (iter_data->lun != -1 && iter_data->lun != task->sc->device->lun) + return true; - spin_unlock_bh(&session->frwd_lock); - iscsi_put_task(task); - spin_lock_bh(&session->frwd_lock); + return iter_data->fn(sc, iter_data, rsvd); +} - spin_lock_bh(&session->back_lock); - } +void iscsi_conn_for_each_sc(struct iscsi_conn *conn, + struct iscsi_sc_iter_data *iter_data) +{ + struct iscsi_session *session = conn->session; + struct Scsi_Host *shost = session->host; + iter_data->conn = conn; + spin_lock_bh(&session->back_lock); + scsi_host_busy_iter(shost, iscsi_sc_iter, iter_data); spin_unlock_bh(&session->back_lock); } +EXPORT_SYMBOL_GPL(iscsi_conn_for_each_sc); + +/* + * Fail commands. session frwd lock held and xmit thread flushed. + */ +static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error) +{ + struct iscsi_sc_iter_data iter_data = { + .lun = lun, + .fn = fail_scsi_task_iter, + .data = &error, + }; + + iscsi_conn_for_each_sc(conn, &iter_data); +} /** * iscsi_suspend_queue - suspend iscsi_queuecommand @@ -2005,14 +2033,51 @@ static int iscsi_has_ping_timed_out(struct iscsi_conn *conn) return 0; } +static bool check_scsi_task_iter(struct scsi_cmnd *sc, void *data, bool rsvd) +{ + struct iscsi_task *task = (struct iscsi_task *)sc->SCp.ptr; + struct iscsi_sc_iter_data *iter_data = data; + struct iscsi_task *timed_out_task = iter_data->data; + + if (task == timed_out_task) + return true; + /* + * Only check if cmds started before this one have made + * progress, or this could never fail + */ + if (time_after(task->sc->jiffies_at_alloc, + timed_out_task->sc->jiffies_at_alloc)) + return true; + + if (time_after(task->last_xfer, timed_out_task->last_timeout)) { + /* + * The timed out task has not made progress, but a task + * started before us has transferred data since we + * started/last-checked. We could be queueing too many tasks + * or the LU is bad. + * + * If the device is bad the cmds ahead of us on other devs will + * complete, and this loop will eventually fail starting the + * scsi eh. + */ + ISCSI_DBG_EH(task->conn->session, + "Command has not made progress but commands ahead of it have. Asking scsi-ml for more time to complete. Our last xfer vs running task last xfer %lu/%lu. Last check %lu.\n", + timed_out_task->last_xfer, task->last_xfer, + timed_out_task->last_timeout); + iter_data->rc = BLK_EH_RESET_TIMER; + return false; + } + return true; +} + enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) { enum blk_eh_timer_return rc = BLK_EH_DONE; - struct iscsi_task *task = NULL, *running_task; + struct iscsi_task *task; struct iscsi_cls_session *cls_session; + struct iscsi_sc_iter_data iter_data; struct iscsi_session *session; struct iscsi_conn *conn; - int i; cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; @@ -2091,45 +2156,16 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) goto done; } - spin_lock(&session->back_lock); - for (i = 0; i < conn->session->cmds_max; i++) { - running_task = conn->session->cmds[i]; - if (!running_task->sc || running_task == task || - running_task->state != ISCSI_TASK_RUNNING) - continue; - - /* - * Only check if cmds started before this one have made - * progress, or this could never fail - */ - if (time_after(running_task->sc->jiffies_at_alloc, - task->sc->jiffies_at_alloc)) - continue; + iter_data.data = task; + iter_data.rc = BLK_EH_DONE; + iter_data.fn = check_scsi_task_iter; + iter_data.lun = -1; - if (time_after(running_task->last_xfer, task->last_timeout)) { - /* - * This task has not made progress, but a task - * started before us has transferred data since - * we started/last-checked. We could be queueing - * too many tasks or the LU is bad. - * - * If the device is bad the cmds ahead of us on - * other devs will complete, and this loop will - * eventually fail starting the scsi eh. - */ - ISCSI_DBG_EH(session, "Command has not made progress " - "but commands ahead of it have. " - "Asking scsi-ml for more time to " - "complete. Our last xfer vs running task " - "last xfer %lu/%lu. Last check %lu.\n", - task->last_xfer, running_task->last_xfer, - task->last_timeout); - spin_unlock(&session->back_lock); - rc = BLK_EH_RESET_TIMER; - goto done; - } + iscsi_conn_for_each_sc(conn, &iter_data); + if (iter_data.rc != BLK_EH_DONE) { + rc = iter_data.rc; + goto done; } - spin_unlock(&session->back_lock); /* Assumes nop timeout is shorter than scsi cmd timeout */ if (task->have_checked_conn) diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 11f0dc74d4c5..5a5f76adbca3 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -469,6 +469,18 @@ extern void iscsi_complete_scsi_task(struct iscsi_task *task, uint32_t exp_cmdsn, uint32_t max_cmdsn); extern int iscsi_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd); +struct iscsi_sc_iter_data { + struct iscsi_conn *conn; + /* optional: if set to -1. It will be ignored */ + u64 lun; + void *data; + int rc; + bool (*fn)(struct scsi_cmnd *sc, void *data, bool rsvd); +}; + +extern void iscsi_conn_for_each_sc(struct iscsi_conn *conn, + struct iscsi_sc_iter_data *iter_data); + /* * generic helpers */ -- 2.25.1