Commit c07d4444 authored by Boaz Harrosh's avatar Boaz Harrosh Committed by James Bottomley

[SCSI] iscsi: bidi support at the generic libiscsi level

- prepare the additional bidi_read rlength header.
- access the right scsi_in() and/or scsi_out() side of things.
  also for resid.
- Handle BIDI underflow overflow from target
Signed-off-by: default avatarBoaz Harrosh <bharrosh@panasas.com>
Reviewed-by: default avatarPete Wyckoff <pw@osc.edu>
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 38d1c069
...@@ -176,6 +176,31 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask) ...@@ -176,6 +176,31 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
return 0; 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 * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
* @ctask: iscsi cmd task * @ctask: iscsi cmd task
...@@ -200,7 +225,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) ...@@ -200,7 +225,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
hdr->flags = ISCSI_ATTR_SIMPLE; hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
hdr->itt = build_itt(ctask->itt, session->age); hdr->itt = build_itt(ctask->itt, session->age);
hdr->data_length = cpu_to_be32(scsi_bufflen(sc));
hdr->cmdsn = cpu_to_be32(session->cmdsn); hdr->cmdsn = cpu_to_be32(session->cmdsn);
session->cmdsn++; session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
...@@ -216,7 +240,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) ...@@ -216,7 +240,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
memcpy(hdr->cdb, sc->cmnd, cmd_len); memcpy(hdr->cdb, sc->cmnd, cmd_len);
ctask->imm_count = 0; 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) { 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; hdr->flags |= ISCSI_FLAG_CMD_WRITE;
/* /*
* Write counters: * Write counters:
...@@ -237,19 +269,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) ...@@ -237,19 +269,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
ctask->unsol_datasn = 0; ctask->unsol_datasn = 0;
if (session->imm_data_en) { 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, ctask->imm_count = min(session->first_burst,
conn->max_xmit_dlength); conn->max_xmit_dlength);
else else
ctask->imm_count = min(scsi_bufflen(sc), ctask->imm_count = min(out_len,
conn->max_xmit_dlength); conn->max_xmit_dlength);
hton24(hdr->dlength, ctask->imm_count); hton24(hdr->dlength, ctask->imm_count);
} else } else
zero_data(hdr->dlength); zero_data(hdr->dlength);
if (!session->initial_r2t_en) { if (!session->initial_r2t_en) {
ctask->unsol_count = min((session->first_burst), ctask->unsol_count = min(session->first_burst, out_len)
(scsi_bufflen(sc))) - ctask->imm_count; - ctask->imm_count;
ctask->unsol_offset = ctask->imm_count; ctask->unsol_offset = ctask->imm_count;
} }
...@@ -259,6 +291,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) ...@@ -259,6 +291,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
} else { } else {
hdr->flags |= ISCSI_FLAG_CMD_FINAL; hdr->flags |= ISCSI_FLAG_CMD_FINAL;
zero_data(hdr->dlength); zero_data(hdr->dlength);
hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
if (sc->sc_data_direction == DMA_FROM_DEVICE) if (sc->sc_data_direction == DMA_FROM_DEVICE)
hdr->flags |= ISCSI_FLAG_CMD_READ; hdr->flags |= ISCSI_FLAG_CMD_READ;
...@@ -277,10 +310,12 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) ...@@ -277,10 +310,12 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
return EIO; return EIO;
conn->scsicmd_pdus_cnt++; conn->scsicmd_pdus_cnt++;
debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
"cmdsn %d win %d]\n", "len %d bidi_len %d cmdsn %d win %d]\n",
scsi_bidi_cmnd(sc) ? "bidirectional" :
sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc), 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); session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
return 0; return 0;
} }
...@@ -343,7 +378,12 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, ...@@ -343,7 +378,12 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
conn->session->tt->cleanup_cmd_task(conn, ctask); conn->session->tt->cleanup_cmd_task(conn, ctask);
sc->result = err; sc->result = err;
if (!scsi_bidi_cmnd(sc))
scsi_set_resid(sc, scsi_bufflen(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) if (conn->ctask == ctask)
conn->ctask = NULL; conn->ctask = NULL;
/* release ref from queuecommand */ /* release ref from queuecommand */
...@@ -478,6 +518,18 @@ invalid_datalen: ...@@ -478,6 +518,18 @@ invalid_datalen:
min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); 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 | if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
ISCSI_FLAG_CMD_OVERFLOW)) { ISCSI_FLAG_CMD_OVERFLOW)) {
int res_count = be32_to_cpu(rhdr->residual_count); int res_count = be32_to_cpu(rhdr->residual_count);
...@@ -485,13 +537,11 @@ invalid_datalen: ...@@ -485,13 +537,11 @@ invalid_datalen:
if (res_count > 0 && if (res_count > 0 &&
(rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
res_count <= scsi_bufflen(sc))) res_count <= scsi_bufflen(sc)))
/* write side for bidi or uni-io set_resid */
scsi_set_resid(sc, res_count); scsi_set_resid(sc, res_count);
else else
sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 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: out:
debug_scsi("done [sc %lx res %d itt 0x%x]\n", debug_scsi("done [sc %lx res %d itt 0x%x]\n",
(long)sc, sc->result, ctask->itt); (long)sc, sc->result, ctask->itt);
...@@ -1147,7 +1197,12 @@ reject: ...@@ -1147,7 +1197,12 @@ reject:
fault: fault:
spin_unlock(&session->lock); spin_unlock(&session->lock);
debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
if (!scsi_bidi_cmnd(sc))
scsi_set_resid(sc, scsi_bufflen(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); sc->scsi_done(sc);
spin_lock(host->host_lock); spin_lock(host->host_lock);
return 0; return 0;
......
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