|
@@ -22,7 +22,6 @@
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
*/
|
|
|
#include <linux/types.h>
|
|
|
-#include <linux/mutex.h>
|
|
|
#include <linux/kfifo.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <asm/unaligned.h>
|
|
@@ -46,27 +45,53 @@ class_to_transport_session(struct iscsi_cls_session *cls_session)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(class_to_transport_session);
|
|
|
|
|
|
-#define INVALID_SN_DELTA 0xffff
|
|
|
+/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
|
|
|
+#define SNA32_CHECK 2147483648UL
|
|
|
|
|
|
-int
|
|
|
-iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
|
|
|
+static int iscsi_sna_lt(u32 n1, u32 n2)
|
|
|
+{
|
|
|
+ return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
|
|
|
+ (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
|
|
|
+}
|
|
|
+
|
|
|
+/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
|
|
|
+static int iscsi_sna_lte(u32 n1, u32 n2)
|
|
|
+{
|
|
|
+ return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
|
|
|
+ (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
|
|
|
{
|
|
|
uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn);
|
|
|
uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn);
|
|
|
|
|
|
- if (max_cmdsn < exp_cmdsn -1 &&
|
|
|
- max_cmdsn > exp_cmdsn - INVALID_SN_DELTA)
|
|
|
- return ISCSI_ERR_MAX_CMDSN;
|
|
|
- if (max_cmdsn > session->max_cmdsn ||
|
|
|
- max_cmdsn < session->max_cmdsn - INVALID_SN_DELTA)
|
|
|
- session->max_cmdsn = max_cmdsn;
|
|
|
- if (exp_cmdsn > session->exp_cmdsn ||
|
|
|
- exp_cmdsn < session->exp_cmdsn - INVALID_SN_DELTA)
|
|
|
+ /*
|
|
|
+ * standard specifies this check for when to update expected and
|
|
|
+ * max sequence numbers
|
|
|
+ */
|
|
|
+ if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (exp_cmdsn != session->exp_cmdsn &&
|
|
|
+ !iscsi_sna_lt(exp_cmdsn, session->exp_cmdsn))
|
|
|
session->exp_cmdsn = exp_cmdsn;
|
|
|
|
|
|
- return 0;
|
|
|
+ if (max_cmdsn != session->max_cmdsn &&
|
|
|
+ !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) {
|
|
|
+ session->max_cmdsn = max_cmdsn;
|
|
|
+ /*
|
|
|
+ * if the window closed with IO queued, then kick the
|
|
|
+ * xmit thread
|
|
|
+ */
|
|
|
+ if (!list_empty(&session->leadconn->xmitqueue) ||
|
|
|
+ __kfifo_len(session->leadconn->mgmtqueue))
|
|
|
+ scsi_queue_work(session->host,
|
|
|
+ &session->leadconn->xmitwork);
|
|
|
+ }
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn);
|
|
|
+EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
|
|
|
|
|
|
void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
|
|
|
struct iscsi_data *hdr)
|
|
@@ -175,8 +200,13 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
|
|
|
}
|
|
|
|
|
|
conn->scsicmd_pdus_cnt++;
|
|
|
+
|
|
|
+ debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "
|
|
|
+ "cmdsn %d win %d]\n",
|
|
|
+ sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
|
|
|
+ conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen,
|
|
|
+ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
|
|
|
|
|
|
/**
|
|
|
* iscsi_complete_command - return command back to scsi-ml
|
|
@@ -205,26 +235,12 @@ 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)
|
|
|
{
|
|
|
if (atomic_dec_and_test(&ctask->refcount))
|
|
|
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
|
|
|
* @conn: iscsi connection
|
|
@@ -236,21 +252,15 @@ static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
|
|
|
* iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and
|
|
|
* then completes the command and task.
|
|
|
**/
|
|
|
-static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
- struct iscsi_cmd_task *ctask, char *data,
|
|
|
- int datalen)
|
|
|
+static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
+ struct iscsi_cmd_task *ctask, char *data,
|
|
|
+ int datalen)
|
|
|
{
|
|
|
- int rc;
|
|
|
struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr;
|
|
|
struct iscsi_session *session = conn->session;
|
|
|
struct scsi_cmnd *sc = ctask->sc;
|
|
|
|
|
|
- rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
|
|
|
- if (rc) {
|
|
|
- sc->result = DID_ERROR << 16;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
|
|
|
conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
|
|
|
|
|
|
sc->result = (DID_OK << 16) | rhdr->cmd_status;
|
|
@@ -302,7 +312,6 @@ out:
|
|
|
conn->scsirsp_pdus_cnt++;
|
|
|
|
|
|
__iscsi_put_ctask(ctask);
|
|
|
- return rc;
|
|
|
}
|
|
|
|
|
|
static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
|
|
@@ -382,8 +391,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
switch(opcode) {
|
|
|
case ISCSI_OP_SCSI_CMD_RSP:
|
|
|
BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
|
|
|
- rc = iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
|
|
|
- datalen);
|
|
|
+ iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
|
|
|
+ datalen);
|
|
|
break;
|
|
|
case ISCSI_OP_SCSI_DATA_IN:
|
|
|
BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
|
|
@@ -406,11 +415,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
|
|
|
opcode, conn->id, mtask->itt, datalen);
|
|
|
|
|
|
- rc = iscsi_check_assign_cmdsn(session,
|
|
|
- (struct iscsi_nopin*)hdr);
|
|
|
- if (rc)
|
|
|
- goto done;
|
|
|
-
|
|
|
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
|
|
|
switch(opcode) {
|
|
|
case ISCSI_OP_LOGOUT_RSP:
|
|
|
if (datalen) {
|
|
@@ -459,10 +464,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
break;
|
|
|
}
|
|
|
} else if (itt == ~0U) {
|
|
|
- rc = iscsi_check_assign_cmdsn(session,
|
|
|
- (struct iscsi_nopin*)hdr);
|
|
|
- if (rc)
|
|
|
- goto done;
|
|
|
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
|
|
|
|
|
|
switch(opcode) {
|
|
|
case ISCSI_OP_NOOP_IN:
|
|
@@ -492,7 +494,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
} else
|
|
|
rc = ISCSI_ERR_BAD_ITT;
|
|
|
|
|
|
-done:
|
|
|
return rc;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
|
|
@@ -579,17 +580,47 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
|
|
|
|
|
|
+static void iscsi_prep_mtask(struct iscsi_conn *conn,
|
|
|
+ struct iscsi_mgmt_task *mtask)
|
|
|
+{
|
|
|
+ struct iscsi_session *session = conn->session;
|
|
|
+ struct iscsi_hdr *hdr = mtask->hdr;
|
|
|
+ struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
|
|
|
+
|
|
|
+ if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
|
|
|
+ hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
|
|
|
+ nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
|
|
|
+ /*
|
|
|
+ * pre-format CmdSN for outgoing PDU.
|
|
|
+ */
|
|
|
+ nop->cmdsn = cpu_to_be32(session->cmdsn);
|
|
|
+ if (hdr->itt != RESERVED_ITT) {
|
|
|
+ hdr->itt = build_itt(mtask->itt, conn->id, session->age);
|
|
|
+ if (conn->c_stage == ISCSI_CONN_STARTED &&
|
|
|
+ !(hdr->opcode & ISCSI_OP_IMMEDIATE))
|
|
|
+ session->cmdsn++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (session->tt->init_mgmt_task)
|
|
|
+ session->tt->init_mgmt_task(conn, mtask);
|
|
|
+
|
|
|
+ debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
|
|
|
+ hdr->opcode, hdr->itt, mtask->data_count);
|
|
|
+}
|
|
|
+
|
|
|
static int iscsi_xmit_mtask(struct iscsi_conn *conn)
|
|
|
{
|
|
|
struct iscsi_hdr *hdr = conn->mtask->hdr;
|
|
|
int rc, was_logout = 0;
|
|
|
|
|
|
+ spin_unlock_bh(&conn->session->lock);
|
|
|
if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) {
|
|
|
conn->session->state = ISCSI_STATE_IN_RECOVERY;
|
|
|
iscsi_block_session(session_to_cls(conn->session));
|
|
|
was_logout = 1;
|
|
|
}
|
|
|
rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask);
|
|
|
+ spin_lock_bh(&conn->session->lock);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
@@ -603,6 +634,45 @@ static int iscsi_xmit_mtask(struct iscsi_conn *conn)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
|
|
|
+{
|
|
|
+ struct iscsi_session *session = conn->session;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check for iSCSI window and take care of CmdSN wrap-around
|
|
|
+ */
|
|
|
+ if (!iscsi_sna_lte(session->cmdsn, session->max_cmdsn)) {
|
|
|
+ debug_scsi("iSCSI CmdSN closed. MaxCmdSN %u CmdSN %u\n",
|
|
|
+ session->max_cmdsn, session->cmdsn);
|
|
|
+ return -ENOSPC;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int iscsi_xmit_ctask(struct iscsi_conn *conn)
|
|
|
+{
|
|
|
+ struct iscsi_cmd_task *ctask = conn->ctask;
|
|
|
+ int rc = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * serialize with TMF AbortTask
|
|
|
+ */
|
|
|
+ if (ctask->state == ISCSI_TASK_ABORTING)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ __iscsi_get_ctask(ctask);
|
|
|
+ spin_unlock_bh(&conn->session->lock);
|
|
|
+ rc = conn->session->tt->xmit_cmd_task(conn, ctask);
|
|
|
+ spin_lock_bh(&conn->session->lock);
|
|
|
+ __iscsi_put_ctask(ctask);
|
|
|
+
|
|
|
+done:
|
|
|
+ if (!rc)
|
|
|
+ /* done with this ctask */
|
|
|
+ conn->ctask = NULL;
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* iscsi_data_xmit - xmit any command into the scheduled connection
|
|
|
* @conn: iscsi connection
|
|
@@ -614,106 +684,79 @@ static int iscsi_xmit_mtask(struct iscsi_conn *conn)
|
|
|
**/
|
|
|
static int iscsi_data_xmit(struct iscsi_conn *conn)
|
|
|
{
|
|
|
- struct iscsi_transport *tt;
|
|
|
int rc = 0;
|
|
|
|
|
|
+ spin_lock_bh(&conn->session->lock);
|
|
|
if (unlikely(conn->suspend_tx)) {
|
|
|
debug_scsi("conn %d Tx suspended!\n", conn->id);
|
|
|
+ spin_unlock_bh(&conn->session->lock);
|
|
|
return -ENODATA;
|
|
|
}
|
|
|
- tt = conn->session->tt;
|
|
|
-
|
|
|
- /*
|
|
|
- * Transmit in the following order:
|
|
|
- *
|
|
|
- * 1) un-finished xmit (ctask or mtask)
|
|
|
- * 2) immediate control PDUs
|
|
|
- * 3) write data
|
|
|
- * 4) SCSI commands
|
|
|
- * 5) non-immediate control PDUs
|
|
|
- *
|
|
|
- * No need to lock around __kfifo_get as long as
|
|
|
- * there's one producer and one consumer.
|
|
|
- */
|
|
|
-
|
|
|
- BUG_ON(conn->ctask && conn->mtask);
|
|
|
|
|
|
if (conn->ctask) {
|
|
|
- iscsi_get_ctask(conn->ctask);
|
|
|
- rc = tt->xmit_cmd_task(conn, conn->ctask);
|
|
|
- iscsi_put_ctask(conn->ctask);
|
|
|
+ rc = iscsi_xmit_ctask(conn);
|
|
|
if (rc)
|
|
|
goto again;
|
|
|
- /* done with this in-progress ctask */
|
|
|
- conn->ctask = NULL;
|
|
|
}
|
|
|
+
|
|
|
if (conn->mtask) {
|
|
|
rc = iscsi_xmit_mtask(conn);
|
|
|
if (rc)
|
|
|
goto again;
|
|
|
}
|
|
|
|
|
|
- /* process immediate first */
|
|
|
- if (unlikely(__kfifo_len(conn->immqueue))) {
|
|
|
- while (__kfifo_get(conn->immqueue, (void*)&conn->mtask,
|
|
|
- sizeof(void*))) {
|
|
|
- spin_lock_bh(&conn->session->lock);
|
|
|
- list_add_tail(&conn->mtask->running,
|
|
|
- &conn->mgmt_run_list);
|
|
|
- spin_unlock_bh(&conn->session->lock);
|
|
|
- rc = iscsi_xmit_mtask(conn);
|
|
|
- if (rc)
|
|
|
- goto again;
|
|
|
- }
|
|
|
+ /*
|
|
|
+ * process mgmt pdus like nops before commands since we should
|
|
|
+ * only have one nop-out as a ping from us and targets should not
|
|
|
+ * overflow us with nop-ins
|
|
|
+ */
|
|
|
+check_mgmt:
|
|
|
+ while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask,
|
|
|
+ sizeof(void*))) {
|
|
|
+ iscsi_prep_mtask(conn, conn->mtask);
|
|
|
+ list_add_tail(&conn->mtask->running, &conn->mgmt_run_list);
|
|
|
+ rc = iscsi_xmit_mtask(conn);
|
|
|
+ if (rc)
|
|
|
+ goto again;
|
|
|
}
|
|
|
|
|
|
/* process command queue */
|
|
|
- spin_lock_bh(&conn->session->lock);
|
|
|
while (!list_empty(&conn->xmitqueue)) {
|
|
|
+ rc = iscsi_check_cmdsn_window_closed(conn);
|
|
|
+ if (rc) {
|
|
|
+ spin_unlock_bh(&conn->session->lock);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
/*
|
|
|
* iscsi tcp may readd the task to the xmitqueue to send
|
|
|
* write data
|
|
|
*/
|
|
|
conn->ctask = list_entry(conn->xmitqueue.next,
|
|
|
struct iscsi_cmd_task, running);
|
|
|
+ if (conn->ctask->state == ISCSI_TASK_PENDING) {
|
|
|
+ iscsi_prep_scsi_cmd_pdu(conn->ctask);
|
|
|
+ conn->session->tt->init_cmd_task(conn->ctask);
|
|
|
+ }
|
|
|
conn->ctask->state = ISCSI_TASK_RUNNING;
|
|
|
list_move_tail(conn->xmitqueue.next, &conn->run_list);
|
|
|
- __iscsi_get_ctask(conn->ctask);
|
|
|
- spin_unlock_bh(&conn->session->lock);
|
|
|
-
|
|
|
- rc = tt->xmit_cmd_task(conn, conn->ctask);
|
|
|
-
|
|
|
- spin_lock_bh(&conn->session->lock);
|
|
|
- __iscsi_put_ctask(conn->ctask);
|
|
|
- if (rc) {
|
|
|
- spin_unlock_bh(&conn->session->lock);
|
|
|
+ rc = iscsi_xmit_ctask(conn);
|
|
|
+ if (rc)
|
|
|
goto again;
|
|
|
- }
|
|
|
+ /*
|
|
|
+ * we could continuously get new ctask requests so
|
|
|
+ * we need to check the mgmt queue for nops that need to
|
|
|
+ * be sent to aviod starvation
|
|
|
+ */
|
|
|
+ if (__kfifo_len(conn->mgmtqueue))
|
|
|
+ goto check_mgmt;
|
|
|
}
|
|
|
spin_unlock_bh(&conn->session->lock);
|
|
|
- /* done with this ctask */
|
|
|
- conn->ctask = NULL;
|
|
|
-
|
|
|
- /* process the rest control plane PDUs, if any */
|
|
|
- if (unlikely(__kfifo_len(conn->mgmtqueue))) {
|
|
|
- while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask,
|
|
|
- sizeof(void*))) {
|
|
|
- spin_lock_bh(&conn->session->lock);
|
|
|
- list_add_tail(&conn->mtask->running,
|
|
|
- &conn->mgmt_run_list);
|
|
|
- spin_unlock_bh(&conn->session->lock);
|
|
|
- rc = iscsi_xmit_mtask(conn);
|
|
|
- if (rc)
|
|
|
- goto again;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
return -ENODATA;
|
|
|
|
|
|
again:
|
|
|
if (unlikely(conn->suspend_tx))
|
|
|
- return -ENODATA;
|
|
|
-
|
|
|
+ rc = -ENODATA;
|
|
|
+ spin_unlock_bh(&conn->session->lock);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
@@ -725,11 +768,9 @@ static void iscsi_xmitworker(struct work_struct *work)
|
|
|
/*
|
|
|
* serialize Xmit worker on a per-connection basis.
|
|
|
*/
|
|
|
- mutex_lock(&conn->xmitmutex);
|
|
|
do {
|
|
|
rc = iscsi_data_xmit(conn);
|
|
|
} while (rc >= 0 || rc == -EAGAIN);
|
|
|
- mutex_unlock(&conn->xmitmutex);
|
|
|
}
|
|
|
|
|
|
enum {
|
|
@@ -787,20 +828,23 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
|
|
|
goto fault;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * Check for iSCSI window and take care of CmdSN wrap-around
|
|
|
- */
|
|
|
- if ((int)(session->max_cmdsn - session->cmdsn) < 0) {
|
|
|
- reason = FAILURE_WINDOW_CLOSED;
|
|
|
- goto reject;
|
|
|
- }
|
|
|
-
|
|
|
conn = session->leadconn;
|
|
|
if (!conn) {
|
|
|
reason = FAILURE_SESSION_FREED;
|
|
|
goto fault;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * We check this here and in data xmit, because if we get to the point
|
|
|
+ * that this check is hitting the window then we have enough IO in
|
|
|
+ * flight and enough IO waiting to be transmitted it is better
|
|
|
+ * to let the scsi/block layer queue up.
|
|
|
+ */
|
|
|
+ if (iscsi_check_cmdsn_window_closed(conn)) {
|
|
|
+ reason = FAILURE_WINDOW_CLOSED;
|
|
|
+ goto reject;
|
|
|
+ }
|
|
|
+
|
|
|
if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
|
|
|
sizeof(void*))) {
|
|
|
reason = FAILURE_OOM;
|
|
@@ -815,17 +859,8 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
|
|
|
ctask->conn = conn;
|
|
|
ctask->sc = sc;
|
|
|
INIT_LIST_HEAD(&ctask->running);
|
|
|
- iscsi_prep_scsi_cmd_pdu(ctask);
|
|
|
-
|
|
|
- session->tt->init_cmd_task(ctask);
|
|
|
|
|
|
list_add_tail(&ctask->running, &conn->xmitqueue);
|
|
|
- debug_scsi(
|
|
|
- "ctask enq [%s cid %d sc %p cdb 0x%x itt 0x%x len %d cmdsn %d "
|
|
|
- "win %d]\n",
|
|
|
- sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
|
|
|
- conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen,
|
|
|
- session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
|
|
|
spin_unlock(&session->lock);
|
|
|
|
|
|
scsi_queue_work(host, &conn->xmitwork);
|
|
@@ -856,19 +891,16 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
|
|
|
|
|
|
-static int
|
|
|
-iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
- char *data, uint32_t data_size)
|
|
|
+static struct iscsi_mgmt_task *
|
|
|
+__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
+ char *data, uint32_t data_size)
|
|
|
{
|
|
|
struct iscsi_session *session = conn->session;
|
|
|
- struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
|
|
|
struct iscsi_mgmt_task *mtask;
|
|
|
|
|
|
- spin_lock_bh(&session->lock);
|
|
|
- if (session->state == ISCSI_STATE_TERMINATE) {
|
|
|
- spin_unlock_bh(&session->lock);
|
|
|
- return -EPERM;
|
|
|
- }
|
|
|
+ if (session->state == ISCSI_STATE_TERMINATE)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
|
|
|
hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
|
|
|
/*
|
|
@@ -882,27 +914,11 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
|
|
|
BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
|
|
|
|
|
|
- nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
|
|
|
if (!__kfifo_get(session->mgmtpool.queue,
|
|
|
- (void*)&mtask, sizeof(void*))) {
|
|
|
- spin_unlock_bh(&session->lock);
|
|
|
- return -ENOSPC;
|
|
|
- }
|
|
|
+ (void*)&mtask, sizeof(void*)))
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * pre-format CmdSN for outgoing PDU.
|
|
|
- */
|
|
|
- if (hdr->itt != RESERVED_ITT) {
|
|
|
- hdr->itt = build_itt(mtask->itt, conn->id, session->age);
|
|
|
- nop->cmdsn = cpu_to_be32(session->cmdsn);
|
|
|
- if (conn->c_stage == ISCSI_CONN_STARTED &&
|
|
|
- !(hdr->opcode & ISCSI_OP_IMMEDIATE))
|
|
|
- session->cmdsn++;
|
|
|
- } else
|
|
|
- /* do not advance CmdSN */
|
|
|
- nop->cmdsn = cpu_to_be32(session->cmdsn);
|
|
|
-
|
|
|
if (data_size) {
|
|
|
memcpy(mtask->data, data, data_size);
|
|
|
mtask->data_count = data_size;
|
|
@@ -911,38 +927,23 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
|
|
|
|
|
INIT_LIST_HEAD(&mtask->running);
|
|
|
memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
|
|
|
- if (session->tt->init_mgmt_task)
|
|
|
- session->tt->init_mgmt_task(conn, mtask, data, data_size);
|
|
|
- spin_unlock_bh(&session->lock);
|
|
|
-
|
|
|
- debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
|
|
|
- hdr->opcode, hdr->itt, data_size);
|
|
|
-
|
|
|
- /*
|
|
|
- * since send_pdu() could be called at least from two contexts,
|
|
|
- * we need to serialize __kfifo_put, so we don't have to take
|
|
|
- * additional lock on fast data-path
|
|
|
- */
|
|
|
- if (hdr->opcode & ISCSI_OP_IMMEDIATE)
|
|
|
- __kfifo_put(conn->immqueue, (void*)&mtask, sizeof(void*));
|
|
|
- else
|
|
|
- __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*));
|
|
|
-
|
|
|
- scsi_queue_work(session->host, &conn->xmitwork);
|
|
|
- return 0;
|
|
|
+ __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*));
|
|
|
+ return mtask;
|
|
|
}
|
|
|
|
|
|
int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
|
|
|
char *data, uint32_t data_size)
|
|
|
{
|
|
|
struct iscsi_conn *conn = cls_conn->dd_data;
|
|
|
- int rc;
|
|
|
-
|
|
|
- mutex_lock(&conn->xmitmutex);
|
|
|
- rc = iscsi_conn_send_generic(conn, hdr, data, data_size);
|
|
|
- mutex_unlock(&conn->xmitmutex);
|
|
|
+ struct iscsi_session *session = conn->session;
|
|
|
+ int err = 0;
|
|
|
|
|
|
- return rc;
|
|
|
+ spin_lock_bh(&session->lock);
|
|
|
+ if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
|
|
|
+ err = -EPERM;
|
|
|
+ spin_unlock_bh(&session->lock);
|
|
|
+ scsi_queue_work(session->host, &conn->xmitwork);
|
|
|
+ return err;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
|
|
|
|
|
@@ -1027,14 +1028,12 @@ static void iscsi_tmabort_timedout(unsigned long data)
|
|
|
spin_unlock(&session->lock);
|
|
|
}
|
|
|
|
|
|
-/* must be called with the mutex lock */
|
|
|
static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
|
|
|
struct iscsi_cmd_task *ctask)
|
|
|
{
|
|
|
struct iscsi_conn *conn = ctask->conn;
|
|
|
struct iscsi_session *session = conn->session;
|
|
|
struct iscsi_tm *hdr = &conn->tmhdr;
|
|
|
- int rc;
|
|
|
|
|
|
/*
|
|
|
* ctask timed out but session is OK requests must be serialized.
|
|
@@ -1047,32 +1046,27 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
|
|
|
hdr->rtt = ctask->hdr->itt;
|
|
|
hdr->refcmdsn = ctask->hdr->cmdsn;
|
|
|
|
|
|
- rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr,
|
|
|
- NULL, 0);
|
|
|
- if (rc) {
|
|
|
+ ctask->mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
|
|
|
+ NULL, 0);
|
|
|
+ if (!ctask->mtask) {
|
|
|
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
|
|
|
- debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt,
|
|
|
- rc);
|
|
|
- return rc;
|
|
|
+ debug_scsi("abort sent failure [itt 0x%x]\n", ctask->itt);
|
|
|
+ return -EPERM;
|
|
|
}
|
|
|
+ ctask->state = ISCSI_TASK_ABORTING;
|
|
|
|
|
|
debug_scsi("abort sent [itt 0x%x]\n", ctask->itt);
|
|
|
|
|
|
- spin_lock_bh(&session->lock);
|
|
|
- ctask->mtask = (struct iscsi_mgmt_task *)
|
|
|
- session->mgmt_cmds[get_itt(hdr->itt) -
|
|
|
- ISCSI_MGMT_ITT_OFFSET];
|
|
|
-
|
|
|
if (conn->tmabort_state == TMABORT_INITIAL) {
|
|
|
conn->tmfcmd_pdus_cnt++;
|
|
|
- conn->tmabort_timer.expires = 10*HZ + jiffies;
|
|
|
+ conn->tmabort_timer.expires = 20*HZ + jiffies;
|
|
|
conn->tmabort_timer.function = iscsi_tmabort_timedout;
|
|
|
conn->tmabort_timer.data = (unsigned long)ctask;
|
|
|
add_timer(&conn->tmabort_timer);
|
|
|
debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt);
|
|
|
}
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
- mutex_unlock(&conn->xmitmutex);
|
|
|
+ scsi_queue_work(session->host, &conn->xmitwork);
|
|
|
|
|
|
/*
|
|
|
* block eh thread until:
|
|
@@ -1089,13 +1083,12 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
|
|
|
if (signal_pending(current))
|
|
|
flush_signals(current);
|
|
|
del_timer_sync(&conn->tmabort_timer);
|
|
|
-
|
|
|
- mutex_lock(&conn->xmitmutex);
|
|
|
+ spin_lock_bh(&session->lock);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * xmit mutex and session lock must be held
|
|
|
+ * session lock must be held
|
|
|
*/
|
|
|
static struct iscsi_mgmt_task *
|
|
|
iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt)
|
|
@@ -1127,7 +1120,7 @@ static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
|
|
|
if (!ctask->mtask)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (!iscsi_remove_mgmt_task(conn->immqueue, ctask->mtask->itt))
|
|
|
+ if (!iscsi_remove_mgmt_task(conn->mgmtqueue, ctask->mtask->itt))
|
|
|
list_del(&ctask->mtask->running);
|
|
|
__kfifo_put(session->mgmtpool.queue, (void*)&ctask->mtask,
|
|
|
sizeof(void*));
|
|
@@ -1136,7 +1129,7 @@ static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * session lock and xmitmutex must be held
|
|
|
+ * session lock must be held
|
|
|
*/
|
|
|
static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
|
|
|
int err)
|
|
@@ -1147,11 +1140,14 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
|
|
|
if (!sc)
|
|
|
return;
|
|
|
|
|
|
- conn->session->tt->cleanup_cmd_task(conn, ctask);
|
|
|
+ if (ctask->state != ISCSI_TASK_PENDING)
|
|
|
+ conn->session->tt->cleanup_cmd_task(conn, ctask);
|
|
|
iscsi_ctask_mtask_cleanup(ctask);
|
|
|
|
|
|
sc->result = err;
|
|
|
sc->resid = sc->request_bufflen;
|
|
|
+ if (conn->ctask == ctask)
|
|
|
+ conn->ctask = NULL;
|
|
|
/* release ref from queuecommand */
|
|
|
__iscsi_put_ctask(ctask);
|
|
|
}
|
|
@@ -1179,7 +1175,6 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
|
|
|
conn->eh_abort_cnt++;
|
|
|
debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
|
|
|
|
|
|
- mutex_lock(&conn->xmitmutex);
|
|
|
spin_lock_bh(&session->lock);
|
|
|
|
|
|
/*
|
|
@@ -1192,9 +1187,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
|
|
|
|
|
|
/* ctask completed before time out */
|
|
|
if (!ctask->sc) {
|
|
|
- spin_unlock_bh(&session->lock);
|
|
|
debug_scsi("sc completed while abort in progress\n");
|
|
|
- goto success_rel_mutex;
|
|
|
+ goto success;
|
|
|
}
|
|
|
|
|
|
/* what should we do here ? */
|
|
@@ -1204,15 +1198,13 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
|
|
|
goto failed;
|
|
|
}
|
|
|
|
|
|
- if (ctask->state == ISCSI_TASK_PENDING)
|
|
|
- goto success_cleanup;
|
|
|
+ if (ctask->state == ISCSI_TASK_PENDING) {
|
|
|
+ fail_command(conn, ctask, DID_ABORT << 16);
|
|
|
+ goto success;
|
|
|
+ }
|
|
|
|
|
|
conn->tmabort_state = TMABORT_INITIAL;
|
|
|
-
|
|
|
- spin_unlock_bh(&session->lock);
|
|
|
rc = iscsi_exec_abort_task(sc, ctask);
|
|
|
- spin_lock_bh(&session->lock);
|
|
|
-
|
|
|
if (rc || sc->SCp.phase != session->age ||
|
|
|
session->state != ISCSI_STATE_LOGGED_IN)
|
|
|
goto failed;
|
|
@@ -1220,45 +1212,44 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
|
|
|
|
|
|
switch (conn->tmabort_state) {
|
|
|
case TMABORT_SUCCESS:
|
|
|
- goto success_cleanup;
|
|
|
+ spin_unlock_bh(&session->lock);
|
|
|
+ /*
|
|
|
+ * clean up task if aborted. grab the recv lock as a writer
|
|
|
+ */
|
|
|
+ write_lock_bh(conn->recv_lock);
|
|
|
+ spin_lock(&session->lock);
|
|
|
+ fail_command(conn, ctask, DID_ABORT << 16);
|
|
|
+ spin_unlock(&session->lock);
|
|
|
+ write_unlock_bh(conn->recv_lock);
|
|
|
+ /*
|
|
|
+ * make sure xmit thread is not still touching the
|
|
|
+ * ctask/scsi_cmnd
|
|
|
+ */
|
|
|
+ scsi_flush_work(session->host);
|
|
|
+ goto success_unlocked;
|
|
|
case TMABORT_NOT_FOUND:
|
|
|
if (!ctask->sc) {
|
|
|
/* ctask completed before tmf abort response */
|
|
|
- spin_unlock_bh(&session->lock);
|
|
|
debug_scsi("sc completed while abort in progress\n");
|
|
|
- goto success_rel_mutex;
|
|
|
+ goto success;
|
|
|
}
|
|
|
/* fall through */
|
|
|
default:
|
|
|
/* timedout or failed */
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
|
|
|
- spin_lock_bh(&session->lock);
|
|
|
- goto failed;
|
|
|
+ goto failed_unlocked;
|
|
|
}
|
|
|
|
|
|
-success_cleanup:
|
|
|
- debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
|
|
|
+success:
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
-
|
|
|
- /*
|
|
|
- * clean up task if aborted. we have the xmitmutex so grab
|
|
|
- * the recv lock as a writer
|
|
|
- */
|
|
|
- write_lock_bh(conn->recv_lock);
|
|
|
- spin_lock(&session->lock);
|
|
|
- fail_command(conn, ctask, DID_ABORT << 16);
|
|
|
- spin_unlock(&session->lock);
|
|
|
- write_unlock_bh(conn->recv_lock);
|
|
|
-
|
|
|
-success_rel_mutex:
|
|
|
- mutex_unlock(&conn->xmitmutex);
|
|
|
+success_unlocked:
|
|
|
+ debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
|
|
|
return SUCCESS;
|
|
|
|
|
|
failed:
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
- mutex_unlock(&conn->xmitmutex);
|
|
|
-
|
|
|
+failed_unlocked:
|
|
|
debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
|
|
|
return FAILED;
|
|
|
}
|
|
@@ -1505,11 +1496,6 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
|
|
|
INIT_LIST_HEAD(&conn->xmitqueue);
|
|
|
|
|
|
/* initialize general immediate & non-immediate PDU commands queue */
|
|
|
- conn->immqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*),
|
|
|
- GFP_KERNEL, NULL);
|
|
|
- if (conn->immqueue == ERR_PTR(-ENOMEM))
|
|
|
- goto immqueue_alloc_fail;
|
|
|
-
|
|
|
conn->mgmtqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*),
|
|
|
GFP_KERNEL, NULL);
|
|
|
if (conn->mgmtqueue == ERR_PTR(-ENOMEM))
|
|
@@ -1533,7 +1519,6 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
|
|
|
conn->login_mtask->data = conn->data = data;
|
|
|
|
|
|
init_timer(&conn->tmabort_timer);
|
|
|
- mutex_init(&conn->xmitmutex);
|
|
|
init_waitqueue_head(&conn->ehwait);
|
|
|
|
|
|
return cls_conn;
|
|
@@ -1544,8 +1529,6 @@ login_mtask_data_alloc_fail:
|
|
|
login_mtask_alloc_fail:
|
|
|
kfifo_free(conn->mgmtqueue);
|
|
|
mgmtqueue_alloc_fail:
|
|
|
- kfifo_free(conn->immqueue);
|
|
|
-immqueue_alloc_fail:
|
|
|
iscsi_destroy_conn(cls_conn);
|
|
|
return NULL;
|
|
|
}
|
|
@@ -1564,10 +1547,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
|
|
|
struct iscsi_session *session = conn->session;
|
|
|
unsigned long flags;
|
|
|
|
|
|
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
|
|
|
- mutex_lock(&conn->xmitmutex);
|
|
|
-
|
|
|
spin_lock_bh(&session->lock);
|
|
|
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
|
|
|
conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
|
|
|
if (session->leadconn == conn) {
|
|
|
/*
|
|
@@ -1578,8 +1559,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
|
|
|
}
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
|
|
|
- mutex_unlock(&conn->xmitmutex);
|
|
|
-
|
|
|
/*
|
|
|
* Block until all in-progress commands for this connection
|
|
|
* time out or fail.
|
|
@@ -1616,7 +1595,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
|
|
|
}
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
|
|
|
- kfifo_free(conn->immqueue);
|
|
|
kfifo_free(conn->mgmtqueue);
|
|
|
|
|
|
iscsi_destroy_conn(cls_conn);
|
|
@@ -1677,8 +1655,7 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
|
|
|
struct iscsi_mgmt_task *mtask, *tmp;
|
|
|
|
|
|
/* handle pending */
|
|
|
- while (__kfifo_get(conn->immqueue, (void*)&mtask, sizeof(void*)) ||
|
|
|
- __kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) {
|
|
|
+ while (__kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) {
|
|
|
if (mtask == conn->login_mtask)
|
|
|
continue;
|
|
|
debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt);
|
|
@@ -1748,12 +1725,12 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
|
|
|
conn->c_stage = ISCSI_CONN_STOPPED;
|
|
|
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
+ scsi_flush_work(session->host);
|
|
|
|
|
|
write_lock_bh(conn->recv_lock);
|
|
|
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
|
|
|
write_unlock_bh(conn->recv_lock);
|
|
|
|
|
|
- mutex_lock(&conn->xmitmutex);
|
|
|
/*
|
|
|
* for connection level recovery we should not calculate
|
|
|
* header digest. conn->hdr_size used for optimization
|
|
@@ -1777,8 +1754,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
|
|
|
fail_all_commands(conn);
|
|
|
flush_control_queues(session, conn);
|
|
|
spin_unlock_bh(&session->lock);
|
|
|
-
|
|
|
- mutex_unlock(&conn->xmitmutex);
|
|
|
}
|
|
|
|
|
|
void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
|