}
EXPORT_SYMBOL_GPL(iscsi_conn_queue_work);
-void
-iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
+static void __iscsi_update_cmdsn(struct iscsi_session *session,
+ uint32_t exp_cmdsn, uint32_t max_cmdsn)
{
- uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn);
- uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn);
-
/*
* standard specifies this check for when to update expected and
* max sequence numbers
iscsi_conn_queue_work(session->leadconn);
}
}
+
+void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
+{
+ __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn),
+ be32_to_cpu(hdr->max_cmdsn));
+}
EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
/**
hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
memcpy(task->lun, hdr->lun, sizeof(task->lun));
- hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn);
- session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
cmd_len = sc->cmd_len;
if (cmd_len < ISCSI_CDB_SIZE)
return -EIO;
task->state = ISCSI_TASK_RUNNING;
+ hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn);
+ session->cmdsn++;
conn->scsicmd_pdus_cnt++;
ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
__iscsi_put_task(task);
}
+/**
+ * iscsi_complete_scsi_task - finish scsi task normally
+ * @task: iscsi task for scsi cmd
+ * @exp_cmdsn: expected cmd sn in cpu format
+ * @max_cmdsn: max cmd sn in cpu format
+ *
+ * This is used when drivers do not need or cannot perform
+ * lower level pdu processing.
+ *
+ * Called with session lock
+ */
+void iscsi_complete_scsi_task(struct iscsi_task *task,
+ uint32_t exp_cmdsn, uint32_t max_cmdsn)
+{
+ struct iscsi_conn *conn = task->conn;
+
+ ISCSI_DBG_SESSION(conn->session, "[itt 0x%x]\n", task->itt);
+
+ conn->last_recv = jiffies;
+ __iscsi_update_cmdsn(conn->session, exp_cmdsn, max_cmdsn);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
+}
+EXPORT_SYMBOL_GPL(iscsi_complete_scsi_task);
+
+
/*
* session lock must be held and if not called for a task that is
* still pending or from the xmit thread, then xmit thread must
}
}
+static int iscsi_nop_out_rsp(struct iscsi_task *task,
+ struct iscsi_nopin *nop, char *data, int datalen)
+{
+ struct iscsi_conn *conn = task->conn;
+ int rc = 0;
+
+ if (conn->ping_task != task) {
+ /*
+ * If this is not in response to one of our
+ * nops then it must be from userspace.
+ */
+ if (iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *)nop,
+ data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+ } else
+ mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
+ return rc;
+}
+
static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, int datalen)
{
struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
struct iscsi_hdr rejected_pdu;
+ int opcode, rc = 0;
conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;
- if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) {
- if (ntoh24(reject->dlength) > datalen)
- return ISCSI_ERR_PROTO;
+ if (ntoh24(reject->dlength) > datalen ||
+ ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) {
+ iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected "
+ "pdu. Invalid data length (pdu dlength "
+ "%u, datalen %d\n", ntoh24(reject->dlength),
+ datalen);
+ return ISCSI_ERR_PROTO;
+ }
+ memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
+ opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK;
- if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
- memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
- iscsi_conn_printk(KERN_ERR, conn,
- "pdu (op 0x%x) rejected "
- "due to DataDigest error.\n",
- rejected_pdu.opcode);
+ switch (reject->reason) {
+ case ISCSI_REASON_DATA_DIGEST_ERROR:
+ iscsi_conn_printk(KERN_ERR, conn,
+ "pdu (op 0x%x itt 0x%x) rejected "
+ "due to DataDigest error.\n",
+ rejected_pdu.itt, opcode);
+ break;
+ case ISCSI_REASON_IMM_CMD_REJECT:
+ iscsi_conn_printk(KERN_ERR, conn,
+ "pdu (op 0x%x itt 0x%x) rejected. Too many "
+ "immediate commands.\n",
+ rejected_pdu.itt, opcode);
+ /*
+ * We only send one TMF at a time so if the target could not
+ * handle it, then it should get fixed (RFC mandates that
+ * a target can handle one immediate TMF per conn).
+ *
+ * For nops-outs, we could have sent more than one if
+ * the target is sending us lots of nop-ins
+ */
+ if (opcode != ISCSI_OP_NOOP_OUT)
+ return 0;
+
+ if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ /*
+ * nop-out in response to target's nop-out rejected.
+ * Just resend.
+ */
+ iscsi_send_nopout(conn,
+ (struct iscsi_nopin*)&rejected_pdu);
+ else {
+ struct iscsi_task *task;
+ /*
+ * Our nop as ping got dropped. We know the target
+ * and transport are ok so just clean up
+ */
+ task = iscsi_itt_to_task(conn, rejected_pdu.itt);
+ if (!task) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "Invalid pdu reject. Could "
+ "not lookup rejected task.\n");
+ rc = ISCSI_ERR_BAD_ITT;
+ } else
+ rc = iscsi_nop_out_rsp(task,
+ (struct iscsi_nopin*)&rejected_pdu,
+ NULL, 0);
}
+ break;
+ default:
+ iscsi_conn_printk(KERN_ERR, conn,
+ "pdu (op 0x%x itt 0x%x) rejected. Reason "
+ "code 0x%x\n", rejected_pdu.itt,
+ rejected_pdu.opcode, reject->reason);
+ break;
}
- return 0;
+ return rc;
}
/**
}
conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
- if (conn->ping_task != task)
- /*
- * If this is not in response to one of our
- * nops then it must be from userspace.
- */
- goto recv_pdu;
-
- mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
- iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
+ rc = iscsi_nop_out_rsp(task, (struct iscsi_nopin*)hdr,
+ data, datalen);
break;
default:
rc = ISCSI_ERR_BAD_OPCODE;
struct iscsi_task *task = conn->task;
int rc;
+ if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx))
+ return -ENODATA;
+
__iscsi_get_task(task);
spin_unlock_bh(&conn->session->lock);
rc = conn->session->tt->xmit_task(task);
int rc = 0;
spin_lock_bh(&conn->session->lock);
- if (unlikely(conn->suspend_tx)) {
+ if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) {
ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n");
spin_unlock_bh(&conn->session->lock);
return -ENODATA;
if (conn->task) {
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
}
/*
}
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
}
/* process pending command queue */
list_add_tail(&conn->task->running,
&conn->cmdqueue);
conn->task = NULL;
- goto again;
+ goto done;
} else
fail_scsi_task(conn->task, DID_ABORT);
continue;
}
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
/*
* we could continuously get new task requests so
* we need to check the mgmt queue for nops that need to
conn->task->state = ISCSI_TASK_RUNNING;
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
if (!list_empty(&conn->mgmtqueue))
goto check_mgmt;
}
spin_unlock_bh(&conn->session->lock);
return -ENODATA;
-again:
- if (unlikely(conn->suspend_tx))
- rc = -ENODATA;
+done:
spin_unlock_bh(&conn->session->lock);
return rc;
}
}
}
if (session->tt->xmit_task(task)) {
+ session->cmdsn--;
reason = FAILURE_SESSION_NOT_READY;
goto prepd_reject;
}