Re: [Qemu-devel] [PATCH v2 10/10] nbd: Minimal structured read for client

2017-10-11 Thread Vladimir Sementsov-Ogievskiy

10.10.2017 11:02, Paolo Bonzini wrote:

On 09/10/2017 19:27, Vladimir Sementsov-Ogievskiy wrote:

+int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
+  &request_ret, qiov, payload);
+
+if (ret < 0) {
+s->quit = true;
+} else {
+/* For assert at loop start in nbd_read_reply_entry */
+if (reply) {
+*reply = s->reply;
+}

reply is always non-NULL, right?


+s->reply.handle = 0;
+ret = request_ret;
+}
  

...


+static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle)
+{
+int ret = 0;
+int i = HANDLE_TO_INDEX(s, handle);
+NBDReply reply;
+bool only_structured = false;
+
+do {
+int rc = nbd_co_receive_one_chunk(s, handle, only_structured,
+  NULL, &reply, NULL);

Is rc > 0 propagated correctly?


+if (rc < 0 || nbd_reply_is_simple(&reply)) {
+if (ret == 0) {
+ret = rc;
+}
+only_structured = true;
+continue;
+}
+} while (!s->quit && nbd_reply_is_structured(&s->reply) &&
+ !(s->reply.structured.flags & NBD_SREP_FLAG_DONE));
+
+s->requests[i].coroutine = NULL;
+
+qemu_co_mutex_lock(&s->send_mutex);
+s->in_flight--;
+qemu_co_queue_next(&s->free_sema);
+qemu_co_mutex_unlock(&s->send_mutex);

The code after the loop is duplicated.

An idea could be to wrap nbd_co_receive_one_chunk with an iterator like

typedef struct NBDReplyChunkIter {
 int ret;
 bool done, only_structured;
} NBDReplyChunkIter;

#define NBD_FOREACH_REPLY_CHUNK(s, handle, iter, qiov, reply, payload, \
 structured)\
 for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \
  nbd_reply_chunk_iter_receive(s, &iter, qiov, &reply, handle,  \
   payload);;)

bool coroutine_fn nbd_reply_chunk_iter_receive(...)
{
 if (s->quit) {
 if (iter->ret == 0) {
 iter->ret = -EIO;
 }
 goto break_loop;
 }
 /* Handle the end of the previous iteration.  */
 if (iter->done) {
 goto break_loop;
 }

 rc = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
   qiov, reply, payload);
 if (rc < 0) {
 assert(s->quit);
 if (iter->ret == 0) {
 iter->ret = rc;
 }
 goto break_loop;
 }

 /* No structured reply?  Do not execute the body of
  * NBD_FOREACH_REPLY_CHUNK.
  */
 if (nbd_reply_is_simple(&s->reply) || s->quit) {
 goto break_loop;
 }

 iter->only_structured = true;
 if (s->reply.structured.flags & NBD_SREP_FLAG_DONE) {
 iter->ret = rc;
 iter->done = true;
 /* Execute the loop body, but this iteration is the last.  */
 return true;
 }

 /* An error reply must have NBD_SREP_FLAG_DONE set, otherwise it is
  * a protocol error (FIXME: is this true?  but if not, how do you
  * cope with multiple error replies?)


unfortunately it's not true, spec doesn't forbid several error replies 
on one request.
Furthermore, we have NBD_REPLY_TYPE_ERROR_OFFSET, which have ability of 
several error chunks it its nature.



  */
 if (rc > 0) {
 s->quit = true;
 iter->ret = -EIO;
 goto break_loop;
 }
 return true;

break_loop:
 iter->done = true;

 s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL;

 qemu_co_mutex_lock(&s->send_mutex);
 s->in_flight--;
 qemu_co_queue_next(&s->free_sema);
 qemu_co_mutex_unlock(&s->send_mutex);
 return false;
}

so this loop could be written like

 FOR_EACH_REPLY_CHUNK(s, handle, iter, NULL, &reply, NULL) {
 if (reply.structured.type != NBD_SREP_TYPE_NONE) {
 /* not allowed reply type */
 s->quit = true;
 break;
 }
 }
 return iter.ret;

and likewise for nbd_co_receive_cmdread_reply.  The iterator is a bit
more complex, but it abstracts all the protocol handling and avoids lots
of duplicated code.

Paolo


+return ret;
+}



--
Best regards,
Vladimir




Re: [Qemu-devel] [PATCH v2 10/10] nbd: Minimal structured read for client

2017-10-11 Thread Vladimir Sementsov-Ogievskiy

10.10.2017 18:00, Paolo Bonzini wrote:

On 10/10/2017 16:55, Vladimir Sementsov-Ogievskiy wrote:

Hmm, would it be simpler just pass a function pointer, which should be
called on each loop iteration?
So, we will return to one common func nbd_co_receive_reply, but with two
additional parameters: func and opaque?

Function pointers typically result in having to pass the state around in
a structure, for all the callers.

An iterator also has to package the state in a structure, but it is only
done once.

So function pointers would be simpler in the beginning, but would not
scale as well.

Paolo


Understand. OK, I'll try your suggestion.

--
Best regards,
Vladimir




Re: [Qemu-devel] [PATCH v2 10/10] nbd: Minimal structured read for client

2017-10-10 Thread Paolo Bonzini
On 10/10/2017 16:55, Vladimir Sementsov-Ogievskiy wrote:
> Hmm, would it be simpler just pass a function pointer, which should be
> called on each loop iteration?
> So, we will return to one common func nbd_co_receive_reply, but with two
> additional parameters: func and opaque?

Function pointers typically result in having to pass the state around in
a structure, for all the callers.

An iterator also has to package the state in a structure, but it is only
done once.

So function pointers would be simpler in the beginning, but would not
scale as well.

Paolo



Re: [Qemu-devel] [PATCH v2 10/10] nbd: Minimal structured read for client

2017-10-10 Thread Vladimir Sementsov-Ogievskiy

10.10.2017 11:02, Paolo Bonzini wrote:

On 09/10/2017 19:27, Vladimir Sementsov-Ogievskiy wrote:

+int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
+  &request_ret, qiov, payload);
+
+if (ret < 0) {
+s->quit = true;
+} else {
+/* For assert at loop start in nbd_read_reply_entry */
+if (reply) {
+*reply = s->reply;
+}

reply is always non-NULL, right?


+s->reply.handle = 0;
+ret = request_ret;
+}
  

...


+static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle)
+{
+int ret = 0;
+int i = HANDLE_TO_INDEX(s, handle);
+NBDReply reply;
+bool only_structured = false;
+
+do {
+int rc = nbd_co_receive_one_chunk(s, handle, only_structured,
+  NULL, &reply, NULL);

Is rc > 0 propagated correctly?


+if (rc < 0 || nbd_reply_is_simple(&reply)) {
+if (ret == 0) {
+ret = rc;
+}
+only_structured = true;
+continue;
+}
+} while (!s->quit && nbd_reply_is_structured(&s->reply) &&
+ !(s->reply.structured.flags & NBD_SREP_FLAG_DONE));
+
+s->requests[i].coroutine = NULL;
+
+qemu_co_mutex_lock(&s->send_mutex);
+s->in_flight--;
+qemu_co_queue_next(&s->free_sema);
+qemu_co_mutex_unlock(&s->send_mutex);

The code after the loop is duplicated.

An idea could be to wrap nbd_co_receive_one_chunk with an iterator like

typedef struct NBDReplyChunkIter {
 int ret;
 bool done, only_structured;
} NBDReplyChunkIter;

#define NBD_FOREACH_REPLY_CHUNK(s, handle, iter, qiov, reply, payload, \
 structured)\
 for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \
  nbd_reply_chunk_iter_receive(s, &iter, qiov, &reply, handle,  \
   payload);;)

bool coroutine_fn nbd_reply_chunk_iter_receive(...)
{
 if (s->quit) {
 if (iter->ret == 0) {
 iter->ret = -EIO;
 }
 goto break_loop;
 }
 /* Handle the end of the previous iteration.  */
 if (iter->done) {
 goto break_loop;
 }

 rc = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
   qiov, reply, payload);
 if (rc < 0) {
 assert(s->quit);
 if (iter->ret == 0) {
 iter->ret = rc;
 }
 goto break_loop;
 }

 /* No structured reply?  Do not execute the body of
  * NBD_FOREACH_REPLY_CHUNK.
  */
 if (nbd_reply_is_simple(&s->reply) || s->quit) {
 goto break_loop;
 }

 iter->only_structured = true;
 if (s->reply.structured.flags & NBD_SREP_FLAG_DONE) {
 iter->ret = rc;
 iter->done = true;
 /* Execute the loop body, but this iteration is the last.  */
 return true;
 }

 /* An error reply must have NBD_SREP_FLAG_DONE set, otherwise it is
  * a protocol error (FIXME: is this true?  but if not, how do you
  * cope with multiple error replies?)
  */
 if (rc > 0) {
 s->quit = true;
 iter->ret = -EIO;
 goto break_loop;
 }
 return true;

break_loop:
 iter->done = true;

 s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL;

 qemu_co_mutex_lock(&s->send_mutex);
 s->in_flight--;
 qemu_co_queue_next(&s->free_sema);
 qemu_co_mutex_unlock(&s->send_mutex);
 return false;
}

so this loop could be written like

 FOR_EACH_REPLY_CHUNK(s, handle, iter, NULL, &reply, NULL) {
 if (reply.structured.type != NBD_SREP_TYPE_NONE) {
 /* not allowed reply type */
 s->quit = true;
 break;
 }
 }
 return iter.ret;

and likewise for nbd_co_receive_cmdread_reply.  The iterator is a bit
more complex, but it abstracts all the protocol handling and avoids lots
of duplicated code.

Paolo


+return ret;
+}


Hmm, would it be simpler just pass a function pointer, which should be 
called on each loop iteration?
So, we will return to one common func nbd_co_receive_reply, but with two 
additional parameters: func and opaque?


--
Best regards,
Vladimir




Re: [Qemu-devel] [PATCH v2 10/10] nbd: Minimal structured read for client

2017-10-10 Thread Vladimir Sementsov-Ogievskiy

09.10.2017 20:27, Vladimir Sementsov-Ogievskiy wrote:

Minimal implementation: for structured error only error_report error
message.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
  include/block/nbd.h |   6 +
  block/nbd-client.c  | 358 
  nbd/client.c|   7 +
  3 files changed, 343 insertions(+), 28 deletions(-)

diff --git a/include/block/nbd.h b/include/block/nbd.h
index 1ef8c8897f..e3350b67a4 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -203,6 +203,11 @@ enum {
  #define NBD_SREP_TYPE_ERROR NBD_SREP_ERR(1)
  #define NBD_SREP_TYPE_ERROR_OFFSET  NBD_SREP_ERR(2)
  
+static inline bool nbd_srep_type_is_error(int type)

+{
+return type & (1 << 15);
+}
+
  /* NBD errors are based on errno numbers, so there is a 1:1 mapping,
   * but only a limited set of errno values is specified in the protocol.
   * Everything else is squashed to EINVAL.
@@ -241,6 +246,7 @@ static inline int nbd_errno_to_system_errno(int err)
  struct NBDExportInfo {
  /* Set by client before nbd_receive_negotiate() */
  bool request_sizes;
+bool structured_reply;
  /* Set by server results during nbd_receive_negotiate() */
  uint64_t size;
  uint16_t flags;
diff --git a/block/nbd-client.c b/block/nbd-client.c
index 58493b7ac4..eda78130a2 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -29,6 +29,7 @@
  
  #include "qemu/osdep.h"

  #include "qapi/error.h"
+#include "qemu/error-report.h"
  #include "nbd-client.h"
  
  #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ (uint64_t)(intptr_t)(bs))

@@ -93,7 +94,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
  if (i >= MAX_NBD_REQUESTS ||
  !s->requests[i].coroutine ||
  !s->requests[i].receiving ||
-nbd_reply_is_structured(&s->reply))
+(nbd_reply_is_structured(&s->reply) && !s->info.structured_reply))
  {
  break;
  }
@@ -181,41 +182,336 @@ err:
  return rc;
  }
  
-static int nbd_co_receive_reply(NBDClientSession *s,

-uint64_t handle,
-QEMUIOVector *qiov)
+static inline void payload_advance16(uint8_t **payload, uint16_t **ptr)
+{
+*ptr = (uint16_t *)*payload;
+be16_to_cpus(*ptr);
+*payload += sizeof(**ptr);
+}
+
+static inline void payload_advance32(uint8_t **payload, uint32_t **ptr)
+{
+*ptr = (uint32_t *)*payload;
+be32_to_cpus(*ptr);
+*payload += sizeof(**ptr);
+}
+
+static inline void payload_advance64(uint8_t **payload, uint64_t **ptr)
+{
+*ptr = (uint64_t *)*payload;
+be64_to_cpus(*ptr);
+*payload += sizeof(**ptr);
+}
+
+static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
+ uint8_t *payload, QEMUIOVector *qiov)
+{
+uint64_t *offset;
+uint32_t *hole_size;
+
+if (chunk->length != sizeof(*offset) + sizeof(*hole_size)) {
+return -EINVAL;
+}
+
+payload_advance64(&payload, &offset);
+payload_advance32(&payload, &hole_size);
+
+if (*offset + *hole_size > qiov->size) {
+return -EINVAL;
+}
+
+qemu_iovec_memset(qiov, *offset, 0, *hole_size);
+
+return 0;
+}
+
+static int nbd_parse_error_payload(NBDStructuredReplyChunk *chunk,
+   uint8_t *payload, int *request_ret)
+{
+uint32_t *error;
+uint16_t *message_size;
+
+assert(chunk->type & (1 << 15));
+
+if (chunk->length < sizeof(error) + sizeof(message_size)) {
+return -EINVAL;
+}
+
+payload_advance32(&payload, &error);
+payload_advance16(&payload, &message_size);
+
+error_report("%.*s", *message_size, payload);
+
+/* TODO add special case for ERROR_OFFSET */
+
+*request_ret = nbd_errno_to_system_errno(*error);
+
+return 0;
+}
+
+static int nbd_co_receive_offset_data_payload(NBDClientSession *s,
+  QEMUIOVector *qiov)
+{
+QEMUIOVector sub_qiov;
+uint64_t offset;
+size_t data_size;
+int ret;
+NBDStructuredReplyChunk *chunk = &s->reply.structured;
+
+assert(nbd_reply_is_structured(&s->reply));
+
+if (chunk->length < sizeof(offset)) {
+return -EINVAL;
+}
+
+if (nbd_read(s->ioc, &offset, sizeof(offset), NULL) < 0) {
+return -EIO;
+}
+be64_to_cpus(&offset);
+
+data_size = chunk->length - sizeof(offset);
+if (offset + data_size > qiov->size) {
+return -EINVAL;
+}
+
+qemu_iovec_init(&sub_qiov, qiov->niov);
+qemu_iovec_concat(&sub_qiov, qiov, offset, data_size);
+ret = qio_channel_readv_all(s->ioc, sub_qiov.iov, sub_qiov.niov, NULL);
+qemu_iovec_destroy(&sub_qiov);
+
+return ret < 0 ? -EIO : 0;
+}
+
+#define NBD_MAX_MALLOC_PAYLOAD 1000
+static int nbd_co_receive_structured_payload(NBDClientSession *s,
+ void **payload)
+{
+int ret;
+uint32_t l

Re: [Qemu-devel] [PATCH v2 10/10] nbd: Minimal structured read for client

2017-10-10 Thread Paolo Bonzini
On 09/10/2017 19:27, Vladimir Sementsov-Ogievskiy wrote:
> 
> +int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
> +  &request_ret, qiov, payload);
> +
> +if (ret < 0) {
> +s->quit = true;
> +} else {
> +/* For assert at loop start in nbd_read_reply_entry */
> +if (reply) {
> +*reply = s->reply;
> +}

reply is always non-NULL, right?

> +s->reply.handle = 0;
> +ret = request_ret;
> +}
>  

...

> +static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle)
> +{
> +int ret = 0;
> +int i = HANDLE_TO_INDEX(s, handle);
> +NBDReply reply;
> +bool only_structured = false;
> +
> +do {
> +int rc = nbd_co_receive_one_chunk(s, handle, only_structured,
> +  NULL, &reply, NULL);

Is rc > 0 propagated correctly?

> +if (rc < 0 || nbd_reply_is_simple(&reply)) {
> +if (ret == 0) {
> +ret = rc;
> +}
> +only_structured = true;
> +continue;
> +}

> +} while (!s->quit && nbd_reply_is_structured(&s->reply) &&
> + !(s->reply.structured.flags & NBD_SREP_FLAG_DONE));
> +
> +s->requests[i].coroutine = NULL;
> +
> +qemu_co_mutex_lock(&s->send_mutex);
> +s->in_flight--;
> +qemu_co_queue_next(&s->free_sema);
> +qemu_co_mutex_unlock(&s->send_mutex);

The code after the loop is duplicated.

An idea could be to wrap nbd_co_receive_one_chunk with an iterator like

typedef struct NBDReplyChunkIter {
int ret;
bool done, only_structured;
} NBDReplyChunkIter;

#define NBD_FOREACH_REPLY_CHUNK(s, handle, iter, qiov, reply, payload, \
structured)\
for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \
 nbd_reply_chunk_iter_receive(s, &iter, qiov, &reply, handle,  \
  payload);;)

bool coroutine_fn nbd_reply_chunk_iter_receive(...)
{
if (s->quit) {
if (iter->ret == 0) {
iter->ret = -EIO;
}
goto break_loop;
}
/* Handle the end of the previous iteration.  */
if (iter->done) {
goto break_loop;
}

rc = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
  qiov, reply, payload);
if (rc < 0) {
assert(s->quit);
if (iter->ret == 0) {
iter->ret = rc;
}
goto break_loop;
}

/* No structured reply?  Do not execute the body of
 * NBD_FOREACH_REPLY_CHUNK.
 */
if (nbd_reply_is_simple(&s->reply) || s->quit) {
goto break_loop;
}

iter->only_structured = true;
if (s->reply.structured.flags & NBD_SREP_FLAG_DONE) {
iter->ret = rc;
iter->done = true;
/* Execute the loop body, but this iteration is the last.  */
return true;
}

/* An error reply must have NBD_SREP_FLAG_DONE set, otherwise it is
 * a protocol error (FIXME: is this true?  but if not, how do you
 * cope with multiple error replies?)
 */
if (rc > 0) {
s->quit = true;
iter->ret = -EIO;
goto break_loop;
}
return true;

break_loop:
iter->done = true;

s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL;

qemu_co_mutex_lock(&s->send_mutex);
s->in_flight--;
qemu_co_queue_next(&s->free_sema);
qemu_co_mutex_unlock(&s->send_mutex);
return false;
}

so this loop could be written like

FOR_EACH_REPLY_CHUNK(s, handle, iter, NULL, &reply, NULL) {
if (reply.structured.type != NBD_SREP_TYPE_NONE) {
/* not allowed reply type */
s->quit = true;
break;
}
}
return iter.ret;

and likewise for nbd_co_receive_cmdread_reply.  The iterator is a bit
more complex, but it abstracts all the protocol handling and avoids lots
of duplicated code.

Paolo

> +return ret;
> +}