diff --git a/examples/iscsiclient.c b/examples/iscsiclient.c index d8f95e9..102d5a3 100644 --- a/examples/iscsiclient.c +++ b/examples/iscsiclient.c @@ -13,7 +13,7 @@ */ /* This is the host/port we connect to.*/ -#define TARGET "127.0.0.1:3260" +#define TARGET "10.1.1.27:3260" #include #include @@ -33,6 +33,8 @@ struct client_state { int block_size; }; +unsigned char small_buffer[512]; + void tm_at_cb(struct iscsi_context *iscsi _U_, int status _U_, void *command_data _U_, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; @@ -105,8 +107,8 @@ void read10_cb(struct iscsi_context *iscsi, int status, void *command_data, void } printf("READ10 successful. Block content:\n"); - for (i=0;idatain.size;i++) { - printf("%02x ", task->datain.data[i]); + for (i=0;i<512;i++) { + printf("%02x ", small_buffer[i]); if (i%16==15) printf("\n"); if (i==69) @@ -152,12 +154,19 @@ void read6_cb(struct iscsi_context *iscsi, int status, void *command_data, void } printf("...\n"); - if (iscsi_read10_task(iscsi, clnt->lun, 0, clnt->block_size, clnt->block_size, read10_cb, private_data) == NULL) { + scsi_free_scsi_task(task); + + if ((task = iscsi_read10_task(iscsi, clnt->lun, 0, clnt->block_size, clnt->block_size, read10_cb, private_data)) == NULL) { printf("failed to send read10 command\n"); - scsi_free_scsi_task(task); exit(10); } - scsi_free_scsi_task(task); + /* provide a buffer from the application to read into instead + * of copying and linearizing the data. This saves two copies + * of the data. One in libiscsi and one in the application + * callback. + */ + scsi_task_add_data_in_buffer(task, 128, &small_buffer[0]); + scsi_task_add_data_in_buffer(task, 512-128, &small_buffer[128]); } void readcapacity10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 8a4e438..4db81e5 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -248,6 +248,9 @@ int iscsi_send_target_nop_out(struct iscsi_context *iscsi, uint32_t ttt); void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...); +unsigned char *iscsi_get_user_in_buffer(struct iscsi_context *iscsi, struct iscsi_in_pdu *in, uint32_t pos, ssize_t *count); +unsigned char *scsi_task_get_data_in_buffer(struct scsi_task *task, uint32_t pos, ssize_t *count); + unsigned long crc32c(char *buf, int len); #endif /* __iscsi_private_h__ */ diff --git a/include/iscsi.h b/include/iscsi.h index 15021e6..40dd5bb 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -626,4 +626,29 @@ struct scsi_task * iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed); + +/* + * This function is used when the application wants to specify its own buffers to read the data + * from the DATA-IN PDUs into. + * The main use is for SCSI read operations to have them write directly into the application buffers to + * avoid the two copies that would occur otherwise. + * First copy from the individual DATA-IN blobs to linearize the buffer and the second in the callback + * to copy the data from the linearized buffer into the application buffer. + * + * This also supports reading into a vector of buffers by calling this function multiple times. + * The individual buffers will be filled in the same order as they were created. + * + * Example: + * task = iscsi_read10_task( ( 2 512byte blocks into two buffers) + * scsi_task_add_data_buffer(task, first_buffer, 512 + * scsi_task_add_data_buffer(task, second_buffer, 512 + * + * + * If you use this function you can not use task->datain in the callback. + * task->datain.size will be 0 and + * task->datain.data will be NULL + */ +int scsi_task_add_data_in_buffer(struct scsi_task *task, int len, unsigned char *buf); + + #endif /* __iscsi_h__ */ diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index fb45bb5..21a78bb 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -148,6 +148,8 @@ struct scsi_task { uint32_t itt; uint32_t cmdsn; uint32_t lun; + + struct scsi_data_buffer *in_buffers; }; void scsi_free_scsi_task(struct scsi_task *task); diff --git a/lib/crc32c.c b/lib/crc32c.c index 96f1c42..1017235 100644 --- a/lib/crc32c.c +++ b/lib/crc32c.c @@ -14,6 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ +#include #include "iscsi.h" #include "iscsi-private.h" diff --git a/lib/nop.c b/lib/nop.c index 33c6502..9d06920 100644 --- a/lib/nop.c +++ b/lib/nop.c @@ -16,6 +16,7 @@ */ #include +#include #include "iscsi.h" #include "iscsi-private.h" diff --git a/lib/scsi-command.c b/lib/scsi-command.c index f2469a5..8cf023d 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -435,12 +435,15 @@ iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, } dsl = ntohl(*(uint32_t *)&in->hdr[4])&0x00ffffff; - if (iscsi_add_data(iscsi, &pdu->indata, - in->data, dsl, 0) - != 0) { - iscsi_set_error(iscsi, "Out-of-memory: failed to add data " + /* Dont add to reassembly buffer if we already have a user buffer */ + if (scsi_task_get_data_in_buffer(task, 0, NULL) == NULL) { + if (iscsi_add_data(iscsi, &pdu->indata, + in->data, dsl, 0) + != 0) { + iscsi_set_error(iscsi, "Out-of-memory: failed to add data " "to pdu in buffer."); - return -1; + return -1; + } } @@ -733,3 +736,29 @@ iscsi_synchronizecache10_task(struct iscsi_context *iscsi, int lun, int lba, return task; } +unsigned char * +iscsi_get_user_in_buffer(struct iscsi_context *iscsi, struct iscsi_in_pdu *in, uint32_t pos, ssize_t *count) +{ + struct iscsi_pdu *pdu; + uint32_t len, offset; + uint32_t itt; + + if ((in->hdr[0] & 0x3f) != ISCSI_PDU_DATA_IN) { + return NULL; + } + + len = ntohl(*(uint32_t *)&in->hdr[4])&0x00ffffff; + offset = ntohl(*(uint32_t *)&in->hdr[40]); + + itt = ntohl(*(uint32_t *)&in->hdr[16]); + for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { + if (pdu->itt == itt) { + break; + } + } + if (pdu == NULL) { + return NULL; + } + + return scsi_task_get_data_in_buffer(pdu->scsi_cbdata->task, offset + pos, count); +} diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 9ba573e..92503c0 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -936,3 +936,50 @@ scsi_get_task_private_ptr(struct scsi_task *task) { return task->ptr; } + + + +struct scsi_data_buffer { + struct scsi_data_buffer *next; + int len; + unsigned char *data; +}; + +int +scsi_task_add_data_in_buffer(struct scsi_task *task, int len, unsigned char *buf) +{ + struct scsi_data_buffer *data_buf; + + data_buf = scsi_malloc(task, sizeof(struct scsi_data_buffer)); + if (data_buf == NULL) { + return -1; + } + + data_buf->len = len; + data_buf->data = buf; + + SLIST_ADD_END(&task->in_buffers, data_buf); + return 0; +} + +unsigned char * +scsi_task_get_data_in_buffer(struct scsi_task *task, uint32_t pos, ssize_t *count) +{ + struct scsi_data_buffer *sdb; + + sdb = task->in_buffers; + if (sdb == NULL) { + return NULL; + } + + while (pos >= sdb->len) { + pos -= sdb->len; + sdb = sdb->next; + } + + if (count && *count > sdb->len - pos) { + *count = sdb->len - pos; + } + + return &sdb->data[pos]; +} diff --git a/lib/socket.c b/lib/socket.c index 30d6ecf..fe957a3 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -267,13 +267,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) data_size = iscsi_get_pdu_data_size(&in->hdr[0]); if (data_size != 0) { - if (in->data == NULL) { - in->data = malloc(data_size); - if (in->data == NULL) { - iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size); - return -1; - } - } + unsigned char *buf = NULL; /* No more data right now */ if (socket_count == 0) { @@ -283,7 +277,22 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) if (count > socket_count) { count = socket_count; } - count = read(iscsi->fd, &in->data[in->data_pos], count); + + /* first try to see if we already have a user buffer */ + buf = iscsi_get_user_in_buffer(iscsi, in, in->data_pos, &count); + /* if not, allocate one */ + if (buf == NULL) { + if (in->data == NULL) { + in->data = malloc(data_size); + if (in->data == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size); + return -1; + } + } + buf = &in->data[in->data_pos]; + } + + count = read(iscsi->fd, buf, count); if (count < 0) { if (errno == EINTR) { return 0; diff --git a/lib/task_mgmt.c b/lib/task_mgmt.c index 31fe44f..d3be124 100644 --- a/lib/task_mgmt.c +++ b/lib/task_mgmt.c @@ -16,6 +16,7 @@ */ #include +#include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h"