diff mbox

[17/18] nbd: BLOCK_STATUS for standard get_block_status function: server part

Message ID 20170203154757.36140-18-vsementsov@virtuozzo.com
State New
Headers show

Commit Message

Vladimir Sementsov-Ogievskiy Feb. 3, 2017, 3:47 p.m. UTC
Minimal realization: only one extent in server answer is supported.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 include/block/nbd.h |  3 ++
 nbd/nbd-internal.h  |  1 +
 nbd/server.c        | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 77 insertions(+), 7 deletions(-)

Comments

Eric Blake Feb. 9, 2017, 3:38 p.m. UTC | #1
On 02/03/2017 09:47 AM, Vladimir Sementsov-Ogievskiy wrote:
> Minimal realization: only one extent in server answer is supported.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  include/block/nbd.h |  3 ++
>  nbd/nbd-internal.h  |  1 +
>  nbd/server.c        | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-----
>  3 files changed, 77 insertions(+), 7 deletions(-)
> 
> diff --git a/include/block/nbd.h b/include/block/nbd.h
> index 08d5e51f21..69aee1eda1 100644
> --- a/include/block/nbd.h
> +++ b/include/block/nbd.h
> @@ -181,6 +181,9 @@ enum {
>  #define NBD_REPLY_TYPE_ERROR ((1 << 15) + 1)
>  #define NBD_REPLY_TYPE_ERROR_OFFSET ((1 << 15) + 2)
>  
> +#define NBD_STATE_HOLE 1
> +#define NBD_STATE_ZERO (1 << 1)

Why not (1 << 0) for NBD_STATE_HOLE?

I almost wonder if it is better to rebase the series to implement
base:allocation first, rather than last, just for easier
interoperability testing with other implementations that still need to
implement structured replies; but I don't think it's a show-stopper.

>  
> +static int blockstatus_to_extent(BlockDriverState *bs, uint64_t offset,
> +                                  uint64_t length, NBDExtent *extent)
> +{
> +    BlockDriverState *file;
> +    uint64_t start_sector = offset >> BDRV_SECTOR_BITS;
> +    uint64_t last_sector = (offset + length - 1) >> BDRV_SECTOR_BITS;

Converting from bytes to sectors by rounding...

> +    uint64_t begin = start_sector;
> +    uint64_t end = last_sector + 1;
> +
> +    int nb = MIN(INT_MAX, end - begin);
> +    int64_t ret = bdrv_get_block_status_above(bs, NULL, begin, nb, &nb, &file);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    extent->flags =
> +        cpu_to_be32((ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
> +                    (ret & BDRV_BLOCK_ZERO      ? NBD_STATE_ZERO : 0));
> +    extent->length = cpu_to_be32((nb << BDRV_SECTOR_BITS) -
> +                                 (offset - (start_sector << BDRV_SECTOR_BITS)));

...then computing the length by undoing the rounding. I really think we
should consider fixing bdrv_get_block_status_above() to be byte-based,
but that's a separate series.  Your calculations look correct in the
meantime, although '(offset & (BDRV_SECTOR_SIZE - 1))' may be a bit
easier to read than '(offset - (start_sector << BDRV_SECTOR_BITS))'.

> @@ -1869,13 +1930,18 @@ static void nbd_trip(void *opaque)
>          break;
>      case NBD_CMD_BLOCK_STATUS:
>          TRACE("Request type is BLOCK_STATUS");
> -        if (client->export_bitmap == NULL) {
> +        if (!!client->export_bitmap) {

!! is not necessary here.

> +            ret = nbd_co_send_bitmap(req->client, request.handle,
> +                                     client->export_bitmap, request.from,
> +                                     request.len, 0);
> +        } else if (client->export_block_status) {
> +            ret = nbd_co_send_block_status(req->client, request.handle,
> +                                           blk_bs(exp->blk), request.from,
> +                                           request.len, 0);

Umm, why are we sending only one status? If the client requests two ids
during NBD_OPT_SET_META_CONTEXT, we should be able to provide both
pieces of information at once.  For a minimal implementation, it works
for proof of concept, but it is pretty restrictive to tell clients that
they can only request a single status context.  I'm fine if we add that
functionality in a later patch, but we'd better have the implementation
ready for the same release as this patch (I still think 2.9 is a
reasonable goal).
Paolo Bonzini Feb. 15, 2017, 5:02 p.m. UTC | #2
On 09/02/2017 16:38, Eric Blake wrote:
>> +static int blockstatus_to_extent(BlockDriverState *bs, uint64_t offset,
>> +                                  uint64_t length, NBDExtent *extent)
>> +{
>> +    BlockDriverState *file;
>> +    uint64_t start_sector = offset >> BDRV_SECTOR_BITS;
>> +    uint64_t last_sector = (offset + length - 1) >> BDRV_SECTOR_BITS;
> Converting from bytes to sectors by rounding...
> 
>> +    uint64_t begin = start_sector;
>> +    uint64_t end = last_sector + 1;
>> +
>> +    int nb = MIN(INT_MAX, end - begin);
>> +    int64_t ret = bdrv_get_block_status_above(bs, NULL, begin, nb, &nb, &file);
>> +    if (ret < 0) {
>> +        return ret;
>> +    }
>> +
>> +    extent->flags =
>> +        cpu_to_be32((ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
>> +                    (ret & BDRV_BLOCK_ZERO      ? NBD_STATE_ZERO : 0));
>> +    extent->length = cpu_to_be32((nb << BDRV_SECTOR_BITS) -
>> +                                 (offset - (start_sector << BDRV_SECTOR_BITS)));
> ...then computing the length by undoing the rounding. I really think we
> should consider fixing bdrv_get_block_status_above() to be byte-based,
> but that's a separate series.  Your calculations look correct in the
> meantime, although '(offset & (BDRV_SECTOR_SIZE - 1))' may be a bit
> easier to read than '(offset - (start_sector << BDRV_SECTOR_BITS))'.

Agreed.  And please make it a separate variable, i.e.

    uint64_t length;

    length = (nb << BDRV_SECTOR_BITS) - (offset & BDRV_SECTOR_SIZE - 1);
    ...
    extent->length = cpu_to_be32(length);

Paolo

Paolo
Paolo Bonzini Feb. 15, 2017, 5:02 p.m. UTC | #3
On 09/02/2017 16:38, Eric Blake wrote:
> Umm, why are we sending only one status? If the client requests two ids
> during NBD_OPT_SET_META_CONTEXT, we should be able to provide both
> pieces of information at once.  For a minimal implementation, it works
> for proof of concept, but it is pretty restrictive to tell clients that
> they can only request a single status context.  I'm fine if we add that
> functionality in a later patch, but we'd better have the implementation
> ready for the same release as this patch (I still think 2.9 is a
> reasonable goal).

Agreed on this too.

Paolo
diff mbox

Patch

diff --git a/include/block/nbd.h b/include/block/nbd.h
index 08d5e51f21..69aee1eda1 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -181,6 +181,9 @@  enum {
 #define NBD_REPLY_TYPE_ERROR ((1 << 15) + 1)
 #define NBD_REPLY_TYPE_ERROR_OFFSET ((1 << 15) + 2)
 
+#define NBD_STATE_HOLE 1
+#define NBD_STATE_ZERO (1 << 1)
+
 #define NBD_MAX_BITMAP_EXTENTS (0x100000 / 8) /* 1 mb of extents data */
 
 ssize_t nbd_wr_syncv(QIOChannel *ioc,
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index fbbcf69925..f89baa6049 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -86,6 +86,7 @@ 
 #define NBD_OPT_LIST_META_CONTEXT (9)
 #define NBD_OPT_SET_META_CONTEXT  (10)
 
+#define NBD_META_NS_BASE "base"
 #define NBD_META_NS_BITMAPS "qemu-dirty-bitmap"
 
 /* NBD errors are based on errno numbers, so there is a 1:1 mapping,
diff --git a/nbd/server.c b/nbd/server.c
index c96dda4086..cb87481382 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -103,6 +103,7 @@  struct NBDClient {
 
     bool structured_reply;
     BdrvDirtyBitmap *export_bitmap;
+    bool export_block_status;
 };
 
 /* That's all folks */
@@ -506,8 +507,24 @@  static int nbd_negotiate_one_bitmap_query(QIOChannel *ioc, BlockDriverState *bs,
     return 0;
 }
 
+static int nbd_negotiate_one_base_query(QIOChannel *ioc, BlockDriverState *bs,
+                                        uint32_t opt, const char *query,
+                                        bool *block_status)
+{
+    if (query[0] == '\0' || strcmp(query, "allocation") == 0) {
+        if (block_status != NULL) {
+            *block_status = true;
+        }
+
+        return nbd_negotiate_send_meta_context(ioc, "base:allocation", opt);
+    }
+
+    return 0;
+}
+
 static int nbd_negotiate_one_meta_query(QIOChannel *ioc, BlockDriverState *bs,
-                                        uint32_t opt, BdrvDirtyBitmap **bitmap)
+                                        uint32_t opt, BdrvDirtyBitmap **bitmap,
+                                        bool *block_status)
 {
     int ret = 0, nb_read;
     char *query, *colon, *namespace, *subquery;
@@ -530,6 +547,9 @@  static int nbd_negotiate_one_meta_query(QIOChannel *ioc, BlockDriverState *bs,
 
     if (strcmp(namespace, NBD_META_NS_BITMAPS) == 0) {
         ret = nbd_negotiate_one_bitmap_query(ioc, bs, opt, subquery, bitmap);
+    } else if (strcmp(namespace, NBD_META_NS_BASE) == 0) {
+        ret = nbd_negotiate_one_base_query(ioc, bs, opt, subquery,
+                                           block_status);
     }
 
 out:
@@ -663,7 +683,8 @@  static int nbd_negotiate_list_meta_context(NBDClient *client, uint32_t length)
 
     for (i = 0; i < nb_queries; ++i) {
         ret = nbd_negotiate_one_meta_query(client->ioc, bs,
-                                           NBD_OPT_LIST_META_CONTEXT, NULL);
+                                           NBD_OPT_LIST_META_CONTEXT, NULL,
+                                           NULL);
         if (ret < 0) {
             return ret;
         }
@@ -712,7 +733,8 @@  static int nbd_negotiate_set_meta_context(NBDClient *client, uint32_t length)
 
     ret = nbd_negotiate_one_meta_query(client->ioc, bs,
                                        NBD_OPT_SET_META_CONTEXT,
-                                       &client->export_bitmap);
+                                       &client->export_bitmap,
+                                       &client->export_block_status);
     if (ret < 0) {
         return ret;
     }
@@ -1497,6 +1519,30 @@  static unsigned add_extents(NBDExtent *extents, unsigned nb_extents,
     return i;
 }
 
+static int blockstatus_to_extent(BlockDriverState *bs, uint64_t offset,
+                                  uint64_t length, NBDExtent *extent)
+{
+    BlockDriverState *file;
+    uint64_t start_sector = offset >> BDRV_SECTOR_BITS;
+    uint64_t last_sector = (offset + length - 1) >> BDRV_SECTOR_BITS;
+    uint64_t begin = start_sector;
+    uint64_t end = last_sector + 1;
+
+    int nb = MIN(INT_MAX, end - begin);
+    int64_t ret = bdrv_get_block_status_above(bs, NULL, begin, nb, &nb, &file);
+    if (ret < 0) {
+        return ret;
+    }
+
+    extent->flags =
+        cpu_to_be32((ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
+                    (ret & BDRV_BLOCK_ZERO      ? NBD_STATE_ZERO : 0));
+    extent->length = cpu_to_be32((nb << BDRV_SECTOR_BITS) -
+                                 (offset - (start_sector << BDRV_SECTOR_BITS)));
+
+    return 0;
+}
+
 static unsigned bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
                                   uint64_t length, NBDExtent *extents,
                                   unsigned nb_extents)
@@ -1589,6 +1635,21 @@  static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
     return ret;
 }
 
+static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
+                                    BlockDriverState *bs, uint64_t offset,
+                                    uint64_t length, uint32_t context_id)
+{
+    int ret;
+    NBDExtent extent;
+
+    ret = blockstatus_to_extent(bs, offset, length, &extent);
+    if (ret < 0) {
+        return nbd_co_send_structured_error(client, handle, -ret);
+    }
+
+    return nbd_co_send_extents(client, handle, &extent, 1, context_id);
+}
+
 /* Collect a client request.  Return 0 if request looks valid, -EAGAIN
  * to keep trying the collection, -EIO to drop connection right away,
  * and any other negative value to report an error to the client
@@ -1869,13 +1930,18 @@  static void nbd_trip(void *opaque)
         break;
     case NBD_CMD_BLOCK_STATUS:
         TRACE("Request type is BLOCK_STATUS");
-        if (client->export_bitmap == NULL) {
+        if (!!client->export_bitmap) {
+            ret = nbd_co_send_bitmap(req->client, request.handle,
+                                     client->export_bitmap, request.from,
+                                     request.len, 0);
+        } else if (client->export_block_status) {
+            ret = nbd_co_send_block_status(req->client, request.handle,
+                                           blk_bs(exp->blk), request.from,
+                                           request.len, 0);
+        } else {
             reply.error = EINVAL;
             goto error_reply;
         }
-        ret = nbd_co_send_bitmap(req->client, request.handle,
-                                 client->export_bitmap, request.from,
-                                 request.len, 0);
         if (ret < 0) {
             goto out;
         }