From 446b1829c80e87f192a5e220075003a9d2e5aafb Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 10 Dec 2012 19:24:28 +0100 Subject: [PATCH] SOCKET use readv/writev to write directly into iovectors This patch adds support for read/writev to directly read and write from/to iovectors. Before this patch on read and write from/to socket the operation was limited by the iovec boundaries. If there is enough data in the buffer or enough buffer space available its now possible to transfer the whole data in one atomic operaion. Signed-off-by: Peter Lieven --- include/iscsi-private.h | 5 +- lib/iscsi-command.c | 33 +++++++----- lib/scsi-lowlevel.c | 56 +------------------ lib/socket.c | 115 +++++++++++++++++++++++++++++++++++----- 4 files changed, 124 insertions(+), 85 deletions(-) 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; }