Batch pdu read in function iscsi_read_from_socket()

iscsi_read_from_socket can currently only read one PDU in each iscsi_service invocation even
if there is more data available on the socket. This patch reads all PDUs until the socket
would block. It enqueues all complete read PDUs and then processes them in order of arrival.

Signed-off-by: Peter Lieven <pl@kamp.de>
This commit is contained in:
Peter Lieven
2017-01-02 15:52:19 +01:00
parent e8afafb32d
commit b81e9a28a6
2 changed files with 87 additions and 84 deletions

View File

@@ -119,6 +119,7 @@ struct iscsi_context {
int tcp_keepintvl;
int tcp_keepidle;
int tcp_syncnt;
int tcp_nonblocking;
int current_phase;
int next_phase;

View File

@@ -126,15 +126,15 @@ void iscsi_decrement_iface_rr() {
iface_rr--;
}
static void set_nonblocking(int fd)
static int set_nonblocking(int fd)
{
#if defined(WIN32)
unsigned long opt = 1;
ioctlsocket(fd, FIONBIO, &opt);
return ioctlsocket(fd, FIONBIO, &opt);
#else
unsigned v;
v = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, v | O_NONBLOCK);
return fcntl(fd, F_SETFL, v | O_NONBLOCK);
#endif
}
@@ -203,7 +203,7 @@ static int iscsi_tcp_connect(struct iscsi_context *iscsi, union socket_address *
iscsi->fd = iscsi->old_iscsi->fd;
}
set_nonblocking(iscsi->fd);
iscsi->tcp_nonblocking = !set_nonblocking(iscsi->fd);
iscsi_set_tcp_keepalive(iscsi, iscsi->tcp_keepidle, iscsi->tcp_keepcnt, iscsi->tcp_keepintvl);
@@ -568,98 +568,101 @@ iscsi_read_from_socket(struct iscsi_context *iscsi)
struct iscsi_in_pdu *in;
ssize_t data_size, count, padding_size;
if (iscsi->incoming == NULL) {
iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu));
iscsi->incoming->hdr = iscsi_szmalloc(iscsi, ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE);
do {
if (iscsi->incoming == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu");
return -1;
}
}
in = iscsi->incoming;
/* first we must read the header, including any digests */
if (in->hdr_pos < ISCSI_HEADER_SIZE) {
/* try to only read the header, the socket is nonblocking, so
* no need to limit the read to what is available in the socket
*/
count = ISCSI_HEADER_SIZE - in->hdr_pos;
count = recv(iscsi->fd, &in->hdr[in->hdr_pos], count, 0);
if (count == 0) {
return -1;
}
if (count < 0) {
if (errno == EINTR || errno == EAGAIN) {
return 0;
iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu));
iscsi->incoming->hdr = iscsi_szmalloc(iscsi, ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE);
if (iscsi->incoming == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu");
return -1;
}
iscsi_set_error(iscsi, "read from socket failed, "
"errno:%d", errno);
return -1;
}
in->hdr_pos += count;
}
in = iscsi->incoming;
if (in->hdr_pos < ISCSI_HEADER_SIZE) {
/* we don't have the full header yet, so return */
return 0;
}
padding_size = iscsi_get_pdu_padding_size(&in->hdr[0]);
data_size = iscsi_get_pdu_data_size(&in->hdr[0]) + padding_size;
if (data_size < 0 || data_size > (ssize_t)iscsi->initiator_max_recv_data_segment_length) {
iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size);
return -1;
}
if (data_size != 0) {
unsigned char padding_buf[3];
unsigned char *buf = padding_buf;
struct scsi_iovector * iovector_in;
count = data_size - in->data_pos;
/* first try to see if we already have a user buffer */
iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in);
if (iovector_in != NULL && count > padding_size) {
uint32_t offset = scsi_get_uint32(&in->hdr[40]);
count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0);
} else {
if (iovector_in == NULL) {
if (in->data == NULL) {
in->data = iscsi_malloc(iscsi, 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;
}
/* first we must read the header, including any digests */
if (in->hdr_pos < ISCSI_HEADER_SIZE) {
/* try to only read the header, the socket is nonblocking, so
* no need to limit the read to what is available in the socket
*/
count = ISCSI_HEADER_SIZE - in->hdr_pos;
count = recv(iscsi->fd, &in->hdr[in->hdr_pos], count, 0);
if (count == 0) {
/* remote side has closed the socket. */
return -1;
}
if (count < 0) {
if (errno == EINTR || errno == EAGAIN) {
break;
}
buf = &in->data[in->data_pos];
iscsi_set_error(iscsi, "read from socket failed, "
"errno:%d", errno);
return -1;
}
count = recv(iscsi->fd, buf, count, 0);
in->hdr_pos += count;
}
if (count == 0) {
if (in->hdr_pos < ISCSI_HEADER_SIZE) {
/* we don't have the full header yet, so return */
break;
}
padding_size = iscsi_get_pdu_padding_size(&in->hdr[0]);
data_size = iscsi_get_pdu_data_size(&in->hdr[0]) + padding_size;
if (data_size < 0 || data_size > (ssize_t)iscsi->initiator_max_recv_data_segment_length) {
iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size);
return -1;
}
if (count < 0) {
if (errno == EINTR || errno == EAGAIN) {
return 0;
if (data_size != 0) {
unsigned char padding_buf[3];
unsigned char *buf = padding_buf;
struct scsi_iovector * iovector_in;
count = data_size - in->data_pos;
/* first try to see if we already have a user buffer */
iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in);
if (iovector_in != NULL && count > padding_size) {
uint32_t offset = scsi_get_uint32(&in->hdr[40]);
count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0);
} else {
if (iovector_in == NULL) {
if (in->data == NULL) {
in->data = iscsi_malloc(iscsi, 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 = recv(iscsi->fd, buf, count, 0);
}
iscsi_set_error(iscsi, "read from socket failed, "
"errno:%d %s", errno,
iscsi_get_error(iscsi));
return -1;
if (count == 0) {
/* remote side has closed the socket. */
return -1;
}
if (count < 0) {
if (errno == EINTR || errno == EAGAIN) {
break;
}
iscsi_set_error(iscsi, "read from socket failed, "
"errno:%d %s", errno,
iscsi_get_error(iscsi));
return -1;
}
in->data_pos += count;
}
in->data_pos += count;
}
if (in->data_pos < data_size) {
return 0;
}
ISCSI_LIST_ADD_END(&iscsi->inqueue, in);
iscsi->incoming = NULL;
if (in->data_pos < data_size) {
break;
}
ISCSI_LIST_ADD_END(&iscsi->inqueue, in);
iscsi->incoming = NULL;
} while (iscsi->is_loggedin && iscsi->tcp_nonblocking);
while (iscsi->inqueue != NULL) {
struct iscsi_in_pdu *current = iscsi->inqueue;
@@ -671,7 +674,6 @@ iscsi_read_from_socket(struct iscsi_context *iscsi)
iscsi_free_iscsi_in_pdu(iscsi, current);
}
return 0;
}