From 71719cd57fc02ddfd91a4a3ca3f469bfb4d221bc Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 27 Oct 2020 00:05:54 -0500 Subject: nbd: Add new qemu:allocation-depth metadata context 'qemu-img map' provides a way to determine which extents of an image come from the top layer vs. inherited from a backing chain. This is useful information worth exposing over NBD. There is a proposal to add a QMP command block-dirty-bitmap-populate which can create a dirty bitmap that reflects allocation information, at which point the qemu:dirty-bitmap:NAME metadata context can expose that information via the creation of a temporary bitmap, but we can shorten the effort by adding a new qemu:allocation-depth metadata context that does the same thing without an intermediate bitmap (this patch does not eliminate the need for that proposal, as it will have other uses as well). While documenting things, remember that although the NBD protocol has NBD_OPT_SET_META_CONTEXT, the rest of its documentation refers to 'metadata context', which is a more apt description of what is actually being used by NBD_CMD_BLOCK_STATUS: the user is requesting metadata by passing one or more context names. So I also touched up some existing wording to prefer the term 'metadata context' where it makes sense. Note that this patch does not actually enable any way to request a server to enable this context; that will come in the next patch. Signed-off-by: Eric Blake Message-Id: <20201027050556.269064-10-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- nbd/server.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 4 deletions(-) (limited to 'nbd/server.c') diff --git a/nbd/server.c b/nbd/server.c index b6841e4554..ebbefcb6d3 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -27,8 +27,9 @@ #include "qemu/units.h" #define NBD_META_ID_BASE_ALLOCATION 0 +#define NBD_META_ID_ALLOCATION_DEPTH 1 /* Dirty bitmaps use 'NBD_META_ID_DIRTY_BITMAP + i', so keep this id last. */ -#define NBD_META_ID_DIRTY_BITMAP 1 +#define NBD_META_ID_DIRTY_BITMAP 2 /* * NBD_MAX_BLOCK_STATUS_EXTENTS: 1 MiB of extents data. An empirical @@ -95,6 +96,7 @@ struct NBDExport { BlockBackend *eject_notifier_blk; Notifier eject_notifier; + bool allocation_depth; BdrvDirtyBitmap **export_bitmaps; size_t nr_export_bitmaps; }; @@ -108,6 +110,7 @@ typedef struct NBDExportMetaContexts { NBDExport *exp; size_t count; /* number of negotiated contexts */ bool base_allocation; /* export base:allocation context (block status) */ + bool allocation_depth; /* export qemu:allocation-depth */ bool *bitmaps; /* * export qemu:dirty-bitmap:, * sized by exp->nr_export_bitmaps @@ -857,7 +860,8 @@ static bool nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, /* nbd_meta_qemu_query * * Handle queries to 'qemu' namespace. For now, only the qemu:dirty-bitmap: - * context is available. Return true if @query has been handled. + * and qemu:allocation-depth contexts are available. Return true if @query + * has been handled. */ static bool nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, const char *query) @@ -871,12 +875,19 @@ static bool nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, if (!*query) { if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + meta->allocation_depth = meta->exp->allocation_depth; memset(meta->bitmaps, 1, meta->exp->nr_export_bitmaps); } trace_nbd_negotiate_meta_query_parse("empty"); return true; } + if (strcmp(query, "allocation-depth") == 0) { + trace_nbd_negotiate_meta_query_parse("allocation-depth"); + meta->allocation_depth = meta->exp->allocation_depth; + return true; + } + if (nbd_strshift(&query, "dirty-bitmap:")) { trace_nbd_negotiate_meta_query_parse("dirty-bitmap:"); if (!*query) { @@ -901,7 +912,7 @@ static bool nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, return true; } - trace_nbd_negotiate_meta_query_skip("not dirty-bitmap"); + trace_nbd_negotiate_meta_query_skip("unknown qemu context"); return true; } @@ -1008,6 +1019,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { /* enable all known contexts */ meta->base_allocation = true; + meta->allocation_depth = meta->exp->allocation_depth; memset(meta->bitmaps, 1, meta->exp->nr_export_bitmaps); } else { for (i = 0; i < nb_queries; ++i) { @@ -1028,6 +1040,16 @@ static int nbd_negotiate_meta_queries(NBDClient *client, count++; } + if (meta->allocation_depth) { + ret = nbd_negotiate_send_meta_context(client, "qemu:allocation-depth", + NBD_META_ID_ALLOCATION_DEPTH, + errp); + if (ret < 0) { + return ret; + } + count++; + } + for (i = 0; i < meta->exp->nr_export_bitmaps; i++) { const char *bm_name; g_autofree char *context = NULL; @@ -2005,6 +2027,29 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, return 0; } +static int blockalloc_to_extents(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, NBDExtentArray *ea) +{ + while (bytes) { + int64_t num; + int ret = bdrv_is_allocated_above(bs, NULL, false, offset, bytes, + &num); + + if (ret < 0) { + return ret; + } + + if (nbd_extent_array_add(ea, num, ret) < 0) { + return 0; + } + + offset += num; + bytes -= num; + } + + return 0; +} + /* * nbd_co_send_extents * @@ -2044,7 +2089,11 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS; g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents); - ret = blockstatus_to_extents(bs, offset, length, ea); + if (context_id == NBD_META_ID_BASE_ALLOCATION) { + ret = blockstatus_to_extents(bs, offset, length, ea); + } else { + ret = blockalloc_to_extents(bs, offset, length, ea); + } if (ret < 0) { return nbd_co_send_structured_error( client, handle, -ret, "can't get block status", errp); @@ -2395,6 +2444,19 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, } } + if (client->export_meta.allocation_depth) { + ret = nbd_co_send_block_status(client, request->handle, + blk_bs(exp->common.blk), + request->from, request->len, + dont_fragment, + !--contexts_remaining, + NBD_META_ID_ALLOCATION_DEPTH, + errp); + if (ret < 0) { + return ret; + } + } + for (i = 0; i < client->exp->nr_export_bitmaps; i++) { if (!client->export_meta.bitmaps[i]) { continue; -- cgit v1.2.3