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.
This commit is contained in:
Ronnie Sahlberg
2011-01-02 17:57:47 +11:00
parent 9a8fca8bea
commit 5e5d6c05c0
3 changed files with 124 additions and 13 deletions

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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;
}