Re: [PATCH 08/10] nbd/server: introduce NBDExtentArray

2019-10-18 Thread Eric Blake

On 10/18/19 11:07 AM, Vladimir Sementsov-Ogievskiy wrote:


   static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
-   NBDExtent *extents, unsigned int nb_extents,
-   uint64_t length, bool last,
-   uint32_t context_id, Error **errp)
+   NBDExtentArray *ea,
+   bool last, uint32_t context_id, Error **errp)
   {
   NBDStructuredMeta chunk;
-
+    size_t len = ea->count * sizeof(ea->extents[0]);
+    g_autofree NBDExtent *extents = g_memdup(ea->extents, len);


Why do we need memdup here?  What's wrong with modifying ea->extents in 
place?...


To not make ea to be IN-OUT parameter.. I don't like functions with side 
effects.
It will break the code if at some point we call nbd_co_send_extents twice on 
same
ea object.

What is the true way? To not memdup, nbd_co_send_extents should consume the 
whole
ea object..

Seems, g_autoptr attribute can't be used for function parameter, gcc complains:
nbd/server.c:1983:32: error: ‘cleanup’ attribute ignored [-Werror=attributes]
   1983 |g_autoptr(NBDExtentArray) ea,
|^

so, is it better
to call nbd_co_send_external(... g_steal_pointer() ...)

and than in nbd_co_send_external do

g_autoptr(NBDExtentArray) local_ea = ea;
NBDExtent *extents = local_ea->extents;

?



No, that makes it worse.  It's that much more confusing to track who is 
allocating what and where it gets cleaned up.


I personally don't see the need to avoid jumping through hoops to avoid 
an in-out parameter (if we're going to rework code later, we'll notice 
that we documented how things are supposed to be used), but if in-out 
parameters bother you, then the approach you used, even with an extra 
memdup(), is the simplest way to maintain, even if it is not the most 
efficient.


--
Eric Blake, Principal Software Engineer
Red Hat, Inc.   +1-919-301-3226
Virtualization:  qemu.org | libvirt.org



Re: [PATCH 08/10] nbd/server: introduce NBDExtentArray

2019-10-18 Thread Vladimir Sementsov-Ogievskiy
09.10.2019 20:02, Eric Blake wrote:
> On 9/30/19 10:15 AM, Vladimir Sementsov-Ogievskiy wrote:
>> Introduce NBDExtentArray class, to handle extents list creation in more
>> controlled way and with less OUT parameters in functions.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy 
>> ---
>>   nbd/server.c | 184 +--
>>   1 file changed, 90 insertions(+), 94 deletions(-)
>>
> 
>> +static void nbd_extent_array_free(NBDExtentArray *ea)
>> +{
>> +    g_free(ea->extents);
>> +    g_free(ea);
>> +}
>> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free);
> 
> Nice to see this getting more popular :)
> 
>> +
>> +static int nbd_extent_array_add(NBDExtentArray *ea,
>> +    uint32_t length, uint32_t flags)
>>   {
> 
>> -    assert(*nb_extents);
>> -    while (remaining_bytes) {
>> +    if (ea->count >= ea->nb_alloc) {
>> +    return -1;
>> +    }
> 
> Returning -1 is not a failure in the protocol, just failure to add any more 
> information to the reply.  A function comment might help, but this looks like 
> a good helper function.

Something like
/*
  * Add extent to NBDExtentArray. If extent can't be added (no available space),
  * return -1.
  */
above the function?

> 
>> +static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
>> +  uint64_t bytes, NBDExtentArray *ea)
>> +{
>> +    while (bytes) {
>>   uint32_t flags;
>>   int64_t num;
>> -    int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
>> -  , NULL, NULL);
>> +    int ret = bdrv_block_status_above(bs, NULL, offset, bytes, ,
>> +  NULL, NULL);
> 
>> +    if (nbd_extent_array_add(ea, num, flags) < 0) {
>> +    return 0;
>>   }
>> -    offset += num;
>> -    remaining_bytes -= num;
>> -    }
>> -    extents_end = extent + 1;
>> -
>> -    for (extent = extents; extent < extents_end; extent++) {
>> -    extent->flags = cpu_to_be32(extent->flags);
>> -    extent->length = cpu_to_be32(extent->length);
>> +    offset += num;
>> +    bytes -= num;
>>   }
>> -    *bytes -= remaining_bytes;
>> -    *nb_extents = extents_end - extents;
>> -
>>   return 0;
> 
> Also looks good (return 0 if we populated until we either ran out of reply 
> space or out of bytes to report on).
> 
>>   static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
>> -   NBDExtent *extents, unsigned int nb_extents,
>> -   uint64_t length, bool last,
>> -   uint32_t context_id, Error **errp)
>> +   NBDExtentArray *ea,
>> +   bool last, uint32_t context_id, Error **errp)
>>   {
>>   NBDStructuredMeta chunk;
>> -
>> +    size_t len = ea->count * sizeof(ea->extents[0]);
>> +    g_autofree NBDExtent *extents = g_memdup(ea->extents, len);
> 
> Why do we need memdup here?  What's wrong with modifying ea->extents in 
> place?...

To not make ea to be IN-OUT parameter.. I don't like functions with side 
effects.
It will break the code if at some point we call nbd_co_send_extents twice on 
same
ea object.

What is the true way? To not memdup, nbd_co_send_extents should consume the 
whole
ea object..

Seems, g_autoptr attribute can't be used for function parameter, gcc complains:
nbd/server.c:1983:32: error: ‘cleanup’ attribute ignored [-Werror=attributes]
  1983 |g_autoptr(NBDExtentArray) ea,
   |^

so, is it better
to call nbd_co_send_external(... g_steal_pointer() ...)

and than in nbd_co_send_external do

g_autoptr(NBDExtentArray) local_ea = ea;
NBDExtent *extents = local_ea->extents;

?


> 
>> +    NBDExtent *extent, *extents_end = extents + ea->count;
>>   struct iovec iov[] = {
>>   {.iov_base = , .iov_len = sizeof(chunk)},
>> -    {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])}
>> +    {.iov_base = extents, .iov_len = len}
>>   };
>> -    trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last);
>> +    for (extent = extents; extent < extents_end; extent++) {
>> +    extent->flags = cpu_to_be32(extent->flags);
>> +    extent->length = cpu_to_be32(extent->length);
>> +    }
>> +
>> +    trace_nbd_co_send_extents(handle, ea->count, context_id, 
>> ea->total_length,
>> +  last);
>>   set_be_chunk(, last ? NBD_REPLY_FLAG_DONE : 0,
>>    NBD_REPLY_TYPE_BLOCK_STATUS,
>>    handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
>> @@ -1994,39 +2012,27 @@ static int nbd_co_send_block_status(NBDClient 
>> *client, uint64_t handle,
>>   {
>>   int ret;
>>   unsigned int nb_extents = dont_fragment ? 1 : 
>> 

Re: [PATCH 08/10] nbd/server: introduce NBDExtentArray

2019-10-09 Thread Eric Blake

On 9/30/19 10:15 AM, Vladimir Sementsov-Ogievskiy wrote:

Introduce NBDExtentArray class, to handle extents list creation in more
controlled way and with less OUT parameters in functions.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
  nbd/server.c | 184 +--
  1 file changed, 90 insertions(+), 94 deletions(-)




+static void nbd_extent_array_free(NBDExtentArray *ea)
+{
+g_free(ea->extents);
+g_free(ea);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free);


Nice to see this getting more popular :)


+
+static int nbd_extent_array_add(NBDExtentArray *ea,
+uint32_t length, uint32_t flags)
  {



-assert(*nb_extents);
-while (remaining_bytes) {
+if (ea->count >= ea->nb_alloc) {
+return -1;
+}


Returning -1 is not a failure in the protocol, just failure to add any 
more information to the reply.  A function comment might help, but this 
looks like a good helper function.



+static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
+  uint64_t bytes, NBDExtentArray *ea)
+{
+while (bytes) {
  uint32_t flags;
  int64_t num;
-int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
-  , NULL, NULL);
+int ret = bdrv_block_status_above(bs, NULL, offset, bytes, ,
+  NULL, NULL);
  



+if (nbd_extent_array_add(ea, num, flags) < 0) {
+return 0;
  }
-offset += num;
-remaining_bytes -= num;
-}
  
-extents_end = extent + 1;

-
-for (extent = extents; extent < extents_end; extent++) {
-extent->flags = cpu_to_be32(extent->flags);
-extent->length = cpu_to_be32(extent->length);
+offset += num;
+bytes -= num;
  }
  
-*bytes -= remaining_bytes;

-*nb_extents = extents_end - extents;
-
  return 0;


Also looks good (return 0 if we populated until we either ran out of 
reply space or out of bytes to report on).



  static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
-   NBDExtent *extents, unsigned int nb_extents,
-   uint64_t length, bool last,
-   uint32_t context_id, Error **errp)
+   NBDExtentArray *ea,
+   bool last, uint32_t context_id, Error **errp)
  {
  NBDStructuredMeta chunk;
-
+size_t len = ea->count * sizeof(ea->extents[0]);
+g_autofree NBDExtent *extents = g_memdup(ea->extents, len);


Why do we need memdup here?  What's wrong with modifying ea->extents in 
place?...



+NBDExtent *extent, *extents_end = extents + ea->count;
  struct iovec iov[] = {
  {.iov_base = , .iov_len = sizeof(chunk)},
-{.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])}
+{.iov_base = extents, .iov_len = len}
  };
  
-trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last);

+for (extent = extents; extent < extents_end; extent++) {
+extent->flags = cpu_to_be32(extent->flags);
+extent->length = cpu_to_be32(extent->length);
+}
+
+trace_nbd_co_send_extents(handle, ea->count, context_id, ea->total_length,
+  last);
  set_be_chunk(, last ? NBD_REPLY_FLAG_DONE : 0,
   NBD_REPLY_TYPE_BLOCK_STATUS,
   handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
@@ -1994,39 +2012,27 @@ static int nbd_co_send_block_status(NBDClient *client, 
uint64_t handle,
  {
  int ret;
  unsigned int nb_extents = dont_fragment ? 1 : 
NBD_MAX_BLOCK_STATUS_EXTENTS;
-NBDExtent *extents = g_new(NBDExtent, nb_extents);
-uint64_t final_length = length;
+g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
  
-ret = blockstatus_to_extents(bs, offset, _length, extents,

- _extents);
+ret = blockstatus_to_extents(bs, offset, length, ea);
  if (ret < 0) {
-g_free(extents);
  return nbd_co_send_structured_error(
  client, handle, -ret, "can't get block status", errp);
  }
  
-ret = nbd_co_send_extents(client, handle, extents, nb_extents,

-  final_length, last, context_id, errp);
-
-g_free(extents);
-
-return ret;
+return nbd_co_send_extents(client, handle, ea, last, context_id, errp);


...especially since ea goes out of scope right after the helper function 
finishes?


Overall looks like a nice refactoring.

Reviewed-by: Eric Blake 

--
Eric Blake, Principal Software Engineer
Red Hat, Inc.   +1-919-301-3226
Virtualization:  qemu.org | libvirt.org



[PATCH 08/10] nbd/server: introduce NBDExtentArray

2019-09-30 Thread Vladimir Sementsov-Ogievskiy
Introduce NBDExtentArray class, to handle extents list creation in more
controlled way and with less OUT parameters in functions.

Signed-off-by: Vladimir Sementsov-Ogievskiy 
---
 nbd/server.c | 184 +--
 1 file changed, 90 insertions(+), 94 deletions(-)

diff --git a/nbd/server.c b/nbd/server.c
index cc0069c15b..cc63d8ad21 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1894,27 +1894,63 @@ static int coroutine_fn 
nbd_co_send_sparse_read(NBDClient *client,
 return ret;
 }
 
-/*
- * Populate @extents from block status. Update @bytes to be the actual
- * length encoded (which may be smaller than the original), and update
- * @nb_extents to the number of extents used.
- *
- * Returns zero on success and -errno on bdrv_block_status_above failure.
- */
-static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
-  uint64_t *bytes, NBDExtent *extents,
-  unsigned int *nb_extents)
+typedef struct NBDExtentArray {
+NBDExtent *extents;
+unsigned int nb_alloc;
+unsigned int count;
+uint64_t total_length;
+} NBDExtentArray;
+
+static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc)
+{
+NBDExtentArray *ea = g_new0(NBDExtentArray, 1);
+
+ea->nb_alloc = nb_alloc;
+ea->extents = g_new(NBDExtent, nb_alloc);
+
+return ea;
+}
+
+static void nbd_extent_array_free(NBDExtentArray *ea)
+{
+g_free(ea->extents);
+g_free(ea);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free);
+
+static int nbd_extent_array_add(NBDExtentArray *ea,
+uint32_t length, uint32_t flags)
 {
-uint64_t remaining_bytes = *bytes;
-NBDExtent *extent = extents, *extents_end = extents + *nb_extents;
-bool first_extent = true;
+if (!length) {
+return 0;
+}
+
+/* Extend previous extent if flags are the same */
+if (ea->count > 0 && flags == ea->extents[ea->count - 1].flags) {
+ea->extents[ea->count - 1].length += length;
+ea->total_length += length;
+return 0;
+}
 
-assert(*nb_extents);
-while (remaining_bytes) {
+if (ea->count >= ea->nb_alloc) {
+return -1;
+}
+
+ea->total_length += length;
+ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags};
+ea->count++;
+
+return 0;
+}
+
+static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
+  uint64_t bytes, NBDExtentArray *ea)
+{
+while (bytes) {
 uint32_t flags;
 int64_t num;
-int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
-  , NULL, NULL);
+int ret = bdrv_block_status_above(bs, NULL, offset, bytes, ,
+  NULL, NULL);
 
 if (ret < 0) {
 return ret;
@@ -1923,60 +1959,42 @@ static int blockstatus_to_extents(BlockDriverState *bs, 
uint64_t offset,
 flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
 (ret & BDRV_BLOCK_ZERO  ? NBD_STATE_ZERO : 0);
 
-if (first_extent) {
-extent->flags = flags;
-extent->length = num;
-first_extent = false;
-} else if (flags == extent->flags) {
-/* extend current extent */
-extent->length += num;
-} else {
-if (extent + 1 == extents_end) {
-break;
-}
-
-/* start new extent */
-extent++;
-extent->flags = flags;
-extent->length = num;
+if (nbd_extent_array_add(ea, num, flags) < 0) {
+return 0;
 }
-offset += num;
-remaining_bytes -= num;
-}
 
-extents_end = extent + 1;
-
-for (extent = extents; extent < extents_end; extent++) {
-extent->flags = cpu_to_be32(extent->flags);
-extent->length = cpu_to_be32(extent->length);
+offset += num;
+bytes -= num;
 }
 
-*bytes -= remaining_bytes;
-*nb_extents = extents_end - extents;
-
 return 0;
 }
 
-/* nbd_co_send_extents
+/*
+ * nbd_co_send_extents
  *
- * @length is only for tracing purposes (and may be smaller or larger
- * than the client's original request). @last controls whether
- * NBD_REPLY_FLAG_DONE is sent. @extents should already be in
- * big-endian format.
+ * @last controls whether NBD_REPLY_FLAG_DONE is sent.
  */
 static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
-   NBDExtent *extents, unsigned int nb_extents,
-   uint64_t length, bool last,
-   uint32_t context_id, Error **errp)
+   NBDExtentArray *ea,
+   bool last, uint32_t context_id, Error **errp)
 {
 NBDStructuredMeta chunk;
-
+size_t len = ea->count *