return 0;
}
+/*
+ * make an extended cdb AHS
+ */
+static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
+{
+ struct scsi_cmnd *cmd = ctask->sc;
+ unsigned rlen, pad_len;
+ unsigned short ahslength;
+ struct iscsi_ecdb_ahdr *ecdb_ahdr;
+ int rc;
+
+ ecdb_ahdr = iscsi_next_hdr(ctask);
+ rlen = cmd->cmd_len - ISCSI_CDB_SIZE;
+
+ BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb));
+ ahslength = rlen + sizeof(ecdb_ahdr->reserved);
+
+ pad_len = iscsi_padding(rlen);
+
+ rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) +
+ sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len);
+ if (rc)
+ return rc;
+
+ if (pad_len)
+ memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len);
+
+ ecdb_ahdr->ahslength = cpu_to_be16(ahslength);
+ ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB;
+ ecdb_ahdr->reserved = 0;
+ memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen);
+
+ debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
+ "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
+ cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len);
+
+ return 0;
+}
+
+static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+{
+ struct scsi_cmnd *sc = ctask->sc;
+ struct iscsi_rlength_ahdr *rlen_ahdr;
+ int rc;
+
+ rlen_ahdr = iscsi_next_hdr(ctask);
+ rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
+ if (rc)
+ return rc;
+
+ rlen_ahdr->ahslength =
+ cpu_to_be16(sizeof(rlen_ahdr->read_length) +
+ sizeof(rlen_ahdr->reserved));
+ rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
+ rlen_ahdr->reserved = 0;
+ rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
+
+ debug_scsi("bidi-in rlen_ahdr->read_length(%d) "
+ "rlen_ahdr->ahslength(%d)\n",
+ be32_to_cpu(rlen_ahdr->read_length),
+ be16_to_cpu(rlen_ahdr->ahslength));
+ return 0;
+}
+
/**
* iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
* @ctask: iscsi cmd task
struct iscsi_session *session = conn->session;
struct iscsi_cmd *hdr = ctask->hdr;
struct scsi_cmnd *sc = ctask->sc;
- unsigned hdrlength;
+ unsigned hdrlength, cmd_len;
int rc;
ctask->hdr_len = 0;
hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
hdr->itt = build_itt(ctask->itt, session->age);
- hdr->data_length = cpu_to_be32(scsi_bufflen(sc));
hdr->cmdsn = cpu_to_be32(session->cmdsn);
session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
- memcpy(hdr->cdb, sc->cmnd, sc->cmd_len);
- if (sc->cmd_len < MAX_COMMAND_SIZE)
- memset(&hdr->cdb[sc->cmd_len], 0,
- MAX_COMMAND_SIZE - sc->cmd_len);
+ cmd_len = sc->cmd_len;
+ if (cmd_len < ISCSI_CDB_SIZE)
+ memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
+ else if (cmd_len > ISCSI_CDB_SIZE) {
+ rc = iscsi_prep_ecdb_ahs(ctask);
+ if (rc)
+ return rc;
+ cmd_len = ISCSI_CDB_SIZE;
+ }
+ memcpy(hdr->cdb, sc->cmnd, cmd_len);
ctask->imm_count = 0;
+ if (scsi_bidi_cmnd(sc)) {
+ hdr->flags |= ISCSI_FLAG_CMD_READ;
+ rc = iscsi_prep_bidi_ahs(ctask);
+ if (rc)
+ return rc;
+ }
if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ unsigned out_len = scsi_out(sc)->length;
+ hdr->data_length = cpu_to_be32(out_len);
hdr->flags |= ISCSI_FLAG_CMD_WRITE;
/*
* Write counters:
ctask->unsol_datasn = 0;
if (session->imm_data_en) {
- if (scsi_bufflen(sc) >= session->first_burst)
+ if (out_len >= session->first_burst)
ctask->imm_count = min(session->first_burst,
conn->max_xmit_dlength);
else
- ctask->imm_count = min(scsi_bufflen(sc),
+ ctask->imm_count = min(out_len,
conn->max_xmit_dlength);
hton24(hdr->dlength, ctask->imm_count);
} else
zero_data(hdr->dlength);
if (!session->initial_r2t_en) {
- ctask->unsol_count = min((session->first_burst),
- (scsi_bufflen(sc))) - ctask->imm_count;
+ ctask->unsol_count = min(session->first_burst, out_len)
+ - ctask->imm_count;
ctask->unsol_offset = ctask->imm_count;
}
} else {
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
zero_data(hdr->dlength);
+ hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
if (sc->sc_data_direction == DMA_FROM_DEVICE)
hdr->flags |= ISCSI_FLAG_CMD_READ;
return EIO;
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, scsi_bufflen(sc),
+ debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
+ "len %d bidi_len %d cmdsn %d win %d]\n",
+ scsi_bidi_cmnd(sc) ? "bidirectional" :
+ sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
+ conn->id, sc, sc->cmnd[0], ctask->itt,
+ scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
return 0;
}
conn->session->tt->cleanup_cmd_task(conn, ctask);
sc->result = err;
- scsi_set_resid(sc, scsi_bufflen(sc));
+ if (!scsi_bidi_cmnd(sc))
+ scsi_set_resid(sc, scsi_bufflen(sc));
+ else {
+ scsi_out(sc)->resid = scsi_out(sc)->length;
+ scsi_in(sc)->resid = scsi_in(sc)->length;
+ }
if (conn->ctask == ctask)
conn->ctask = NULL;
/* release ref from queuecommand */
min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
}
+ if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
+ ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
+ int res_count = be32_to_cpu(rhdr->bi_residual_count);
+
+ if (scsi_bidi_cmnd(sc) && res_count > 0 &&
+ (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
+ res_count <= scsi_in(sc)->length))
+ scsi_in(sc)->resid = res_count;
+ else
+ sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+ }
+
if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
ISCSI_FLAG_CMD_OVERFLOW)) {
int res_count = be32_to_cpu(rhdr->residual_count);
if (res_count > 0 &&
(rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
res_count <= scsi_bufflen(sc)))
+ /* write side for bidi or uni-io set_resid */
scsi_set_resid(sc, res_count);
else
sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
- } else if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
- ISCSI_FLAG_CMD_BIDI_OVERFLOW))
- sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
-
+ }
out:
debug_scsi("done [sc %lx res %d itt 0x%x]\n",
(long)sc, sc->result, ctask->itt);
if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
datalen))
rc = ISCSI_ERR_CONN_FAILED;
- }
+ } else
+ mod_timer(&conn->transport_timer,
+ jiffies + conn->recv_timeout);
iscsi_free_mgmt_task(conn, mtask);
break;
default:
fault:
spin_unlock(&session->lock);
debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
- scsi_set_resid(sc, scsi_bufflen(sc));
+ if (!scsi_bidi_cmnd(sc))
+ scsi_set_resid(sc, scsi_bufflen(sc));
+ else {
+ scsi_out(sc)->resid = scsi_out(sc)->length;
+ scsi_in(sc)->resid = scsi_in(sc)->length;
+ }
sc->scsi_done(sc);
spin_lock(host->host_lock);
return 0;
{
struct iscsi_conn *conn = (struct iscsi_conn *)data;
struct iscsi_session *session = conn->session;
- unsigned long timeout, next_timeout = 0, last_recv;
+ unsigned long recv_timeout, next_timeout = 0, last_recv;
spin_lock(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN)
goto done;
- timeout = conn->recv_timeout;
- if (!timeout)
+ recv_timeout = conn->recv_timeout;
+ if (!recv_timeout)
goto done;
- timeout *= HZ;
+ recv_timeout *= HZ;
last_recv = conn->last_recv;
- if (time_before_eq(last_recv + timeout + (conn->ping_timeout * HZ),
+ if (conn->ping_mtask &&
+ time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
jiffies)) {
iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
"expired, last rx %lu, last ping %lu, "
return;
}
- if (time_before_eq(last_recv + timeout, jiffies)) {
- if (time_before_eq(conn->last_ping, last_recv)) {
- /* send a ping to try to provoke some traffic */
- debug_scsi("Sending nopout as ping on conn %p\n", conn);
- iscsi_send_nopout(conn, NULL);
- }
- next_timeout = last_recv + timeout + (conn->ping_timeout * HZ);
+ if (time_before_eq(last_recv + recv_timeout, jiffies)) {
+ /* send a ping to try to provoke some traffic */
+ debug_scsi("Sending nopout as ping on conn %p\n", conn);
+ iscsi_send_nopout(conn, NULL);
+ next_timeout = conn->last_ping + (conn->ping_timeout * HZ);
} else
- next_timeout = last_recv + timeout;
+ next_timeout = last_recv + recv_timeout;
debug_scsi("Setting next tmo %lu\n", next_timeout);
mod_timer(&conn->transport_timer, next_timeout);