Commit 60ecebf5 authored by Mike Christie's avatar Mike Christie Committed by James Bottomley

[SCSI] add refcouting around ctask usage in main IO patch

It is possible that a ctask could be completing and getting
cleaned up at the same time, we are finishing up the last
data transfer. This could then result in the data transfer
code using stale or invalid values. This patch adds a refcount
to the ctask. When the count goes to zero then we know the
transmit thread and recv thread or softirq are not touching
it and we can safely release it.

The eh should not need to grab a reference because it only cleans
up a task if it has both the xmit mutex and recv lock (or recv
side suspended).
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent ffd0436e
...@@ -179,16 +179,15 @@ EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu); ...@@ -179,16 +179,15 @@ EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
/** /**
* iscsi_complete_command - return command back to scsi-ml * iscsi_complete_command - return command back to scsi-ml
* @session: iscsi session
* @ctask: iscsi cmd task * @ctask: iscsi cmd task
* *
* Must be called with session lock. * Must be called with session lock.
* This function returns the scsi command to scsi-ml and returns * This function returns the scsi command to scsi-ml and returns
* the cmd task to the pool of available cmd tasks. * the cmd task to the pool of available cmd tasks.
*/ */
static void iscsi_complete_command(struct iscsi_session *session, static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
struct iscsi_cmd_task *ctask)
{ {
struct iscsi_session *session = ctask->conn->session;
struct scsi_cmnd *sc = ctask->sc; struct scsi_cmnd *sc = ctask->sc;
ctask->state = ISCSI_TASK_COMPLETED; ctask->state = ISCSI_TASK_COMPLETED;
...@@ -198,6 +197,35 @@ static void iscsi_complete_command(struct iscsi_session *session, ...@@ -198,6 +197,35 @@ static void iscsi_complete_command(struct iscsi_session *session,
sc->scsi_done(sc); sc->scsi_done(sc);
} }
static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask)
{
atomic_inc(&ctask->refcount);
}
static void iscsi_get_ctask(struct iscsi_cmd_task *ctask)
{
spin_lock_bh(&ctask->conn->session->lock);
__iscsi_get_ctask(ctask);
spin_unlock_bh(&ctask->conn->session->lock);
}
static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
{
struct iscsi_conn *conn = ctask->conn;
if (atomic_dec_and_test(&ctask->refcount)) {
conn->session->tt->cleanup_cmd_task(conn, ctask);
iscsi_complete_command(ctask);
}
}
static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
{
spin_lock_bh(&ctask->conn->session->lock);
__iscsi_put_ctask(ctask);
spin_unlock_bh(&ctask->conn->session->lock);
}
/** /**
* iscsi_cmd_rsp - SCSI Command Response processing * iscsi_cmd_rsp - SCSI Command Response processing
* @conn: iscsi connection * @conn: iscsi connection
...@@ -274,7 +302,7 @@ static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ...@@ -274,7 +302,7 @@ static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
(long)sc, sc->result, ctask->itt); (long)sc, sc->result, ctask->itt);
conn->scsirsp_pdus_cnt++; conn->scsirsp_pdus_cnt++;
iscsi_complete_command(conn->session, ctask); __iscsi_put_ctask(ctask);
return rc; return rc;
} }
...@@ -338,7 +366,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ...@@ -338,7 +366,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
BUG_ON((void*)ctask != ctask->sc->SCp.ptr); BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
conn->scsirsp_pdus_cnt++; conn->scsirsp_pdus_cnt++;
iscsi_complete_command(session, ctask); __iscsi_put_ctask(ctask);
} }
break; break;
case ISCSI_OP_R2T: case ISCSI_OP_R2T:
...@@ -563,7 +591,9 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) ...@@ -563,7 +591,9 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
BUG_ON(conn->ctask && conn->mtask); BUG_ON(conn->ctask && conn->mtask);
if (conn->ctask) { if (conn->ctask) {
iscsi_get_ctask(conn->ctask);
rc = tt->xmit_cmd_task(conn, conn->ctask); rc = tt->xmit_cmd_task(conn, conn->ctask);
iscsi_put_ctask(conn->ctask);
if (rc) if (rc)
goto again; goto again;
/* done with this in-progress ctask */ /* done with this in-progress ctask */
...@@ -604,12 +634,19 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) ...@@ -604,12 +634,19 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
struct iscsi_cmd_task, running); struct iscsi_cmd_task, running);
conn->ctask->state = ISCSI_TASK_RUNNING; conn->ctask->state = ISCSI_TASK_RUNNING;
list_move_tail(conn->xmitqueue.next, &conn->run_list); list_move_tail(conn->xmitqueue.next, &conn->run_list);
__iscsi_get_ctask(conn->ctask);
spin_unlock_bh(&conn->session->lock); spin_unlock_bh(&conn->session->lock);
rc = tt->xmit_cmd_task(conn, conn->ctask); rc = tt->xmit_cmd_task(conn, conn->ctask);
if (rc) if (rc)
goto again; goto again;
spin_lock_bh(&conn->session->lock); spin_lock_bh(&conn->session->lock);
__iscsi_put_ctask(conn->ctask);
if (rc) {
spin_unlock_bh(&conn->session->lock);
goto again;
}
} }
spin_unlock_bh(&conn->session->lock); spin_unlock_bh(&conn->session->lock);
/* done with this ctask */ /* done with this ctask */
...@@ -659,6 +696,7 @@ enum { ...@@ -659,6 +696,7 @@ enum {
FAILURE_SESSION_FAILED, FAILURE_SESSION_FAILED,
FAILURE_SESSION_FREED, FAILURE_SESSION_FREED,
FAILURE_WINDOW_CLOSED, FAILURE_WINDOW_CLOSED,
FAILURE_OOM,
FAILURE_SESSION_TERMINATE, FAILURE_SESSION_TERMINATE,
FAILURE_SESSION_IN_RECOVERY, FAILURE_SESSION_IN_RECOVERY,
FAILURE_SESSION_RECOVERY_TIMEOUT, FAILURE_SESSION_RECOVERY_TIMEOUT,
...@@ -717,10 +755,15 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) ...@@ -717,10 +755,15 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
conn = session->leadconn; conn = session->leadconn;
__kfifo_get(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
sizeof(void*))) {
reason = FAILURE_OOM;
goto reject;
}
sc->SCp.phase = session->age; sc->SCp.phase = session->age;
sc->SCp.ptr = (char *)ctask; sc->SCp.ptr = (char *)ctask;
atomic_set(&ctask->refcount, 1);
ctask->state = ISCSI_TASK_PENDING; ctask->state = ISCSI_TASK_PENDING;
ctask->mtask = NULL; ctask->mtask = NULL;
ctask->conn = conn; ctask->conn = conn;
...@@ -1057,13 +1100,11 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, ...@@ -1057,13 +1100,11 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
sc = ctask->sc; sc = ctask->sc;
if (!sc) if (!sc)
return; return;
conn->session->tt->cleanup_cmd_task(conn, ctask);
iscsi_ctask_mtask_cleanup(ctask); iscsi_ctask_mtask_cleanup(ctask);
sc->result = err; sc->result = err;
sc->resid = sc->request_bufflen; sc->resid = sc->request_bufflen;
iscsi_complete_command(conn->session, ctask); __iscsi_put_ctask(ctask);
} }
int iscsi_eh_abort(struct scsi_cmnd *sc) int iscsi_eh_abort(struct scsi_cmnd *sc)
......
...@@ -112,6 +112,7 @@ struct iscsi_cmd_task { ...@@ -112,6 +112,7 @@ struct iscsi_cmd_task {
/* state set/tested under session->lock */ /* state set/tested under session->lock */
int state; int state;
atomic_t refcount;
struct list_head running; /* running cmd list */ struct list_head running; /* running cmd list */
void *dd_data; /* driver/transport data */ void *dd_data; /* driver/transport data */
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment