diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 68b4463..9345b09 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -290,9 +290,8 @@ 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, ...) __attribute__((format(printf, 2, 3))); -unsigned char *iscsi_get_user_in_buffer(struct iscsi_context *iscsi, struct iscsi_in_pdu *in, uint32_t pos, ssize_t *count); -unsigned char *iscsi_get_user_out_buffer(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, uint32_t pos, ssize_t *count); - +struct scsi_iovector *iscsi_get_scsi_task_iovector_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); +struct scsi_iovector *iscsi_get_scsi_task_iovector_out(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); inline void* iscsi_malloc(struct iscsi_context *iscsi, size_t size); inline void* iscsi_zmalloc(struct iscsi_context *iscsi, size_t size); diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index 00e9286..c2c754d 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -436,7 +436,7 @@ iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, dsl = scsi_get_uint32(&in->hdr[4]) & 0x00ffffff; /* Dont add to reassembly buffer if we already have a user buffer */ - if (scsi_task_get_data_in_buffer(task, 0, NULL) == NULL) { + if (task->iovector_in.iov == 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."); @@ -1422,30 +1422,42 @@ iscsi_unmap_task(struct iscsi_context *iscsi, int lun, int anchor, int group, 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 scsi_iovector * +iscsi_get_scsi_task_iovector_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { struct iscsi_pdu *pdu; - uint32_t offset; uint32_t itt; if ((in->hdr[0] & 0x3f) != ISCSI_PDU_DATA_IN) { return NULL; } - offset = scsi_get_uint32(&in->hdr[40]); - itt = scsi_get_uint32(&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); + if (pdu->scsi_cbdata.task->iovector_in.iov == NULL) { + return NULL; + } + + return &pdu->scsi_cbdata.task->iovector_in; +} + +struct scsi_iovector * +iscsi_get_scsi_task_iovector_out(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu) +{ + if (pdu->scsi_cbdata.task->iovector_out.iov == NULL) { + return NULL; + } + + return &pdu->scsi_cbdata.task->iovector_out; } struct scsi_task * @@ -1593,10 +1605,3 @@ iscsi_scsi_cancel_all_tasks(struct iscsi_context *iscsi) iscsi_free_pdu(iscsi, pdu); } } - -unsigned char * -iscsi_get_user_out_buffer(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu, uint32_t pos, ssize_t *count) -{ - return scsi_task_get_data_out_buffer(pdu->scsi_cbdata.task, pos, count); -} - diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index eeb0375..dae85de 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "slist.h" #include "scsi-lowlevel.h" @@ -2593,69 +2594,14 @@ scsi_iovector_add(struct scsi_task *task, struct scsi_iovector *iovector, int le return 0; } -unsigned char * -scsi_iovector_get_buffer(struct scsi_iovector *iovector, uint32_t pos, ssize_t *count) -{ - if (iovector->iov == NULL) { - return NULL; - } - - if (pos == 0 && count == NULL) return iovector->iov[0].iov_base; - - if (pos < iovector->offset) { - /* start over in case we are going backwards */ - iovector->offset = 0; - iovector->consumed = 0; - } - - if (iovector->niov <= iovector->consumed) { - /* someone issued a read but did not provide enough user buffers for all the data. - * maybe someone tried to read just 512 bytes off a MMC device? - */ - return NULL; - } - - struct scsi_iovec *iov = &iovector->iov[iovector->consumed]; - - pos-= iovector->offset; - - while (pos >= iov->iov_len) { - iovector->offset += iov->iov_len; - iovector->consumed++; - pos -= iov->iov_len; - if (iovector->niov <= iovector->consumed) { - return NULL; - } - iov = &iovector->iov[iovector->consumed]; - } - - if (count && *count >= (ssize_t)(iov->iov_len - pos)) { - *count = iov->iov_len - pos; - } - - return (unsigned char *) iov->iov_base + pos; -} - int scsi_task_add_data_in_buffer(struct scsi_task *task, int len, unsigned char *buf) { return scsi_iovector_add(task, &task->iovector_in, len, buf); } -unsigned char * -scsi_task_get_data_in_buffer(struct scsi_task *task, uint32_t pos, ssize_t *count) -{ - return scsi_iovector_get_buffer(&task->iovector_in, pos, count); -} - int scsi_task_add_data_out_buffer(struct scsi_task *task, int len, unsigned char *buf) { return scsi_iovector_add(task, &task->iovector_out, len, buf); } - -unsigned char * -scsi_task_get_data_out_buffer(struct scsi_task *task, uint32_t pos, ssize_t *count) -{ - return scsi_iovector_get_buffer(&task->iovector_out, pos, count); -} diff --git a/lib/socket.c b/lib/socket.c index 6812b90..b448ff5 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -42,6 +42,7 @@ #include #include #include +#include "scsi-lowlevel.h" #include "iscsi.h" #include "iscsi-private.h" #include "slist.h" @@ -363,6 +364,91 @@ iscsi_queue_length(struct iscsi_context *iscsi) return i; } +ssize_t +iscsi_iovector_readv_writev(struct iscsi_context *iscsi, struct scsi_iovector *iovector, uint32_t pos, ssize_t max_read, int do_write) +{ + if (iovector->iov == NULL) { + errno = EINVAL; + return -1; + } + + if (pos < iovector->offset) { + /* start over in case we are going backwards */ + iovector->offset = 0; + iovector->consumed = 0; + } + + if (iovector->niov <= iovector->consumed) { + /* someone issued a read/write but did not provide enough user buffers for all the data. + * maybe someone tried to read just 512 bytes off a MMC device? + */ + errno = EINVAL; + return -1; + } + + /* iov is a pointer to the first iovec to pass */ + struct scsi_iovec *iov = &iovector->iov[iovector->consumed]; + pos -= iovector->offset; + + /* forward until iov points to the first iov to pass */ + while (pos >= iov->iov_len) { + iovector->offset += iov->iov_len; + iovector->consumed++; + pos -= iov->iov_len; + if (iovector->niov <= iovector->consumed) { + errno = EINVAL; + return -1; + } + iov = &iovector->iov[iovector->consumed]; + } + + /* iov2 is a pointer to the last iovec to pass */ + struct scsi_iovec *iov2 = iov; + + int niov=1; /* number of iovectors to pass */ + uint32_t len2 = pos + max_read; /* adjust length of iov2 */ + + /* forward until iov2 points to the last iovec we pass later. it might + happen that we have a lot of iovectors but are limited by max_read */ + while (len2 > iov2->iov_len) { + if (iovector->niov <= iovector->consumed+niov-1) { + errno = EINVAL; + return -1; + } + niov++; + len2 -= iov2->iov_len; + iov2 = &iovector->iov[iovector->consumed+niov-1]; + } + + /* we might limit the length of the last iovec we pass to readv/writev + store its orignal length to restore it later */ + size_t _len2 = iov2->iov_len; + + /* adjust base+len of start iovec and len of last iovec */ + iov2->iov_len = len2; + iov->iov_base = (void*) ((uintptr_t)iov->iov_base + pos); + iov->iov_len -= pos; + + ssize_t n; + if (do_write) { + n = writev(iscsi->fd, (struct iovec*) iov, niov); + } else { + n = readv(iscsi->fd, (struct iovec*) iov, niov); + } + + /* restore original values */ + iov->iov_base = (void*) ((uintptr_t)iov->iov_base - pos); + iov->iov_len += pos; + iov2->iov_len = _len2; + + if (n > max_read) { + /* we read/write more bytes than expected, this MUST not happen */ + errno = EINVAL; + return -1; + } + return n; +} + static int iscsi_read_from_socket(struct iscsi_context *iscsi) { @@ -411,13 +497,16 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) } if (data_size != 0) { unsigned char *buf = NULL; + struct scsi_iovector * iovector_in; count = data_size - in->data_pos; /* 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) { + iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in); + if (iovector_in != NULL) { + uint32_t offset = scsi_get_uint32(&in->hdr[40]); + count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count, 0); + } else { if (in->data == NULL) { in->data = iscsi_malloc(iscsi, data_size); if (in->data == NULL) { @@ -426,9 +515,9 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) } } buf = &in->data[in->data_pos]; + count = recv(iscsi->fd, buf, count, 0); } - - count = recv(iscsi->fd, buf, count, 0); + if (count == 0) { return -1; } @@ -440,6 +529,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) "errno:%d", errno); return -1; } + in->data_pos += count; } @@ -524,19 +614,17 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) /* Write any iovectors that might have been passed to us */ while (pdu->out_written < pdu->out_len) { - unsigned char *buf; + struct scsi_iovector* iovector_out; - count = pdu->out_len - pdu->out_written; - buf = iscsi_get_user_out_buffer(iscsi, pdu, pdu->out_offset + pdu->out_written, &count); - if (buf == NULL) { + iovector_out = iscsi_get_scsi_task_iovector_out(iscsi, pdu); + + if (iovector_out == NULL) { iscsi_set_error(iscsi, "Can't find iovector data for DATA-OUT"); return -1; } - count = send(iscsi->fd, - buf, - count, - 0); + count = iscsi_iovector_readv_writev(iscsi, iovector_out, pdu->out_offset + pdu->out_written, pdu->out_len - pdu->out_written, 1); + if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; @@ -545,6 +633,7 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) "socket :%d", errno); return -1; } + pdu->out_written += count; }