From 5e5d6c05c0e62d25c5c8d2a441d1ed0f1cfc7a27 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 2 Jan 2011 17:57:47 +1100 Subject: [PATCH] Add a function to send DATAOUT PDUs. Update the send pdu command to trap when we need to send data to the target but we are not allowed to send using immediate data. For this case, send the data as a separate DATAOUT pdu instead. Twiddle the flags and other fields to now manage that we send the data as two separate PDUs. --- include/iscsi-private.h | 15 ++++- lib/pdu.c | 2 + lib/scsi-command.c | 120 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 124 insertions(+), 13 deletions(-) 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; }