diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 74053e2..222c0e7 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -138,13 +138,15 @@ enum iscsi_opcode { ISCSI_PDU_SCSI_REQUEST = 0x01, ISCSI_PDU_LOGIN_REQUEST = 0x03, ISCSI_PDU_TEXT_REQUEST = 0x04, + ISCSI_PDU_DATA_OUT = 0x05, ISCSI_PDU_LOGOUT_REQUEST = 0x06, ISCSI_PDU_NOP_IN = 0x20, ISCSI_PDU_SCSI_RESPONSE = 0x21, ISCSI_PDU_LOGIN_RESPONSE = 0x23, ISCSI_PDU_TEXT_RESPONSE = 0x24, ISCSI_PDU_DATA_IN = 0x25, - ISCSI_PDU_LOGOUT_RESPONSE = 0x26 + ISCSI_PDU_LOGOUT_RESPONSE = 0x26, + ISCSI_PDU_NO_PDU = 0xff }; struct iscsi_pdu { @@ -156,6 +158,7 @@ struct iscsi_pdu { #define ISCSI_PDU_NO_CALLBACK 0x00000002 uint32_t flags; + uint32_t lun; uint32_t itt; uint32_t cmdsn; enum iscsi_opcode response_opcode; @@ -164,9 +167,11 @@ struct iscsi_pdu { void *private_data; int written; - struct iscsi_data outdata; + struct iscsi_data outdata; /* Header and Immediate Data */ struct iscsi_data indata; + struct iscsi_data nidata; /* Non-Immediate Data */ + struct iscsi_scsi_cbdata *scsi_cbdata; }; @@ -223,9 +228,15 @@ int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); +int iscsi_send_data_out(struct iscsi_context *iscsi, + struct iscsi_pdu *pdu, + uint32_t offset, + uint32_t len); + void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...); unsigned long crc32c(char *buf, int len); void iscsi_cbdata_steal_scsi_task(struct scsi_task *task); + diff --git a/lib/pdu.c b/lib/pdu.c index 8effa29..93739c7 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -74,6 +74,8 @@ iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, return iscsi_allocate_pdu_with_itt_flags(iscsi, opcode, response_opcode, iscsi->itt++, 0); } + + void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { diff --git a/lib/scsi-command.c b/lib/scsi-command.c index 62f5838..0b39764 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -22,6 +22,7 @@ #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" +#include "slist.h" struct iscsi_scsi_cbdata { struct iscsi_scsi_cbdata *prev, *next; @@ -79,16 +80,93 @@ iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status, } } +int +iscsi_send_data_out(struct iscsi_context *iscsi, struct iscsi_pdu *cmd_pdu, + uint32_t offset, uint32_t len) +{ + struct iscsi_pdu *pdu; + int flags; + + pdu = iscsi_allocate_pdu_with_itt_flags(iscsi, ISCSI_PDU_DATA_OUT, + ISCSI_PDU_NO_PDU, + cmd_pdu->itt, + ISCSI_PDU_DELETE_WHEN_SENT|ISCSI_PDU_NO_CALLBACK); + if (pdu == NULL) { + iscsi_set_error(iscsi, "Out-of-memory, Failed to allocate " + "scsi data out pdu."); + SLIST_REMOVE(&iscsi->outqueue, cmd_pdu); + SLIST_REMOVE(&iscsi->waitpdu, cmd_pdu); + cmd_pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, + cmd_pdu->private_data); + iscsi_free_pdu(iscsi, cmd_pdu); + return -1; + } + + flags = ISCSI_PDU_SCSI_FINAL; + + /* flags */ + iscsi_pdu_set_pduflags(pdu, flags); + + /* lun */ + iscsi_pdu_set_lun(pdu, cmd_pdu->lun); + + /* ttt */ + iscsi_pdu_set_ttt(pdu, 0xffffffff); + + /* exp statsn */ + iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1); + + /* data sn */ + iscsi_pdu_set_datasn(pdu, 0); + + /* buffer offset */ + iscsi_pdu_set_bufferoffset(pdu, offset); + + if (iscsi_pdu_add_data(iscsi, pdu, cmd_pdu->nidata.data + offset, len) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to " + "add outdata to the pdu."); + SLIST_REMOVE(&iscsi->outqueue, cmd_pdu); + SLIST_REMOVE(&iscsi->waitpdu, cmd_pdu); + cmd_pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, + cmd_pdu->private_data); + iscsi_free_pdu(iscsi, cmd_pdu); + iscsi_free_pdu(iscsi, pdu); + return -1; + } + + pdu->callback = cmd_pdu->callback; + pdu->private_data = cmd_pdu->private_data;; + + if (iscsi_queue_pdu(iscsi, pdu) != 0) { + iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " + "scsi pdu."); + SLIST_REMOVE(&iscsi->outqueue, cmd_pdu); + SLIST_REMOVE(&iscsi->waitpdu, cmd_pdu); + cmd_pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, + cmd_pdu->private_data); + iscsi_free_pdu(iscsi, cmd_pdu); + iscsi_free_pdu(iscsi, pdu); + return -1; + } + return 0; +} + int iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, struct scsi_task *task, iscsi_command_cb cb, - struct iscsi_data *data, void *private_data) + struct iscsi_data *d, void *private_data) { struct iscsi_pdu *pdu; struct iscsi_scsi_cbdata *scsi_cbdata; + struct iscsi_data data; + int flags; + data.data = (d != NULL) ? d->data : NULL; + data.size = (d != NULL) ? d->size : 0; + if (iscsi->session_type != ISCSI_SESSION_NORMAL) { iscsi_set_error(iscsi, "Trying to send command on " "discovery session."); @@ -137,34 +215,49 @@ iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, break; case SCSI_XFER_WRITE: flags |= ISCSI_PDU_SCSI_WRITE; - if (data == NULL) { + if (data.size == 0) { iscsi_set_error(iscsi, "DATA-OUT command but data " "== NULL."); iscsi_free_pdu(iscsi, pdu); return -1; } - if (data->size != task->expxferlen) { + if (data.size != task->expxferlen) { iscsi_set_error(iscsi, "Data size:%d is not same as " "expected data transfer " - "length:%d.", data->size, + "length:%d.", data.size, task->expxferlen); iscsi_free_pdu(iscsi, pdu); return -1; } - if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: Failed to " - "add outdata to the pdu."); - iscsi_free_pdu(iscsi, pdu); - return -1; + if (iscsi->use_immediate_data == ISCSI_IMMEDIATE_DATA_NO) { + pdu->nidata.data = data.data; + pdu->nidata.size = data.size; + data.data = NULL; + data.size = 0; } + /* Only add data to the out-pdu if we actually have some immediate data to attach */ + if (data.size > 0) { + if (iscsi_pdu_add_data(iscsi, pdu, data.data, data.size) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to " + "add outdata to the pdu."); + iscsi_free_pdu(iscsi, pdu); + return -1; + } + } + + if (pdu->nidata.size > 0) { + /* We have more data to send, so dont flag this PDU as final */ + flags &= ~ISCSI_PDU_SCSI_FINAL; + } break; } iscsi_pdu_set_pduflags(pdu, flags); /* lun */ iscsi_pdu_set_lun(pdu, lun); + pdu->lun = lun; /* expxferlen */ iscsi_pdu_set_expxferlen(pdu, task->expxferlen); @@ -176,7 +269,7 @@ iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, /* exp statsn */ iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1); - + /* cdb */ iscsi_pdu_set_cdb(pdu, task); @@ -190,6 +283,11 @@ iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, return -1; } + /* Can we send some unsolicited data ? */ + if (pdu->nidata.size != 0 && iscsi->use_initial_r2t == ISCSI_INITIAL_R2T_NO) { + iscsi_send_data_out(iscsi, pdu, 0, pdu->nidata.size); + } + return 0; }