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 <pl@kamp.de>
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
115
lib/socket.c
115
lib/socket.c
@@ -42,6 +42,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user