|
|
ae23c9 |
From e058bf5e47af745997f99aa21c003e40f50bddcc Mon Sep 17 00:00:00 2001
|
|
|
ae23c9 |
From: John Snow <jsnow@redhat.com>
|
|
|
ae23c9 |
Date: Wed, 18 Jul 2018 22:55:03 +0200
|
|
|
ae23c9 |
Subject: [PATCH 245/268] nbd/server: implement dirty bitmap export
|
|
|
ae23c9 |
|
|
|
ae23c9 |
RH-Author: John Snow <jsnow@redhat.com>
|
|
|
ae23c9 |
Message-id: <20180718225511.14878-28-jsnow@redhat.com>
|
|
|
ae23c9 |
Patchwork-id: 81412
|
|
|
ae23c9 |
O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 27/35] nbd/server: implement dirty bitmap export
|
|
|
ae23c9 |
Bugzilla: 1207657
|
|
|
ae23c9 |
RH-Acked-by: Eric Blake <eblake@redhat.com>
|
|
|
ae23c9 |
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
ae23c9 |
RH-Acked-by: Fam Zheng <famz@redhat.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Handle a new NBD meta namespace: "qemu", and corresponding queries:
|
|
|
ae23c9 |
"qemu:dirty-bitmap:<export bitmap name>".
|
|
|
ae23c9 |
|
|
|
ae23c9 |
With the new metadata context negotiated, BLOCK_STATUS query will reply
|
|
|
ae23c9 |
with dirty-bitmap data, converted to extents. The new public function
|
|
|
ae23c9 |
nbd_export_bitmap selects which bitmap to export. For now, only one bitmap
|
|
|
ae23c9 |
may be exported.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
|
|
ae23c9 |
Message-Id: <20180609151758.17343-5-vsementsov@virtuozzo.com>
|
|
|
ae23c9 |
Reviewed-by: Eric Blake <eblake@redhat.com>
|
|
|
ae23c9 |
[eblake: wording tweaks, minor cleanups, additional tracing]
|
|
|
ae23c9 |
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
|
ae23c9 |
(cherry picked from commit 3d068aff16d6dbf066328977c5152847a62f2a0a)
|
|
|
ae23c9 |
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
ae23c9 |
---
|
|
|
ae23c9 |
include/block/nbd.h | 8 +-
|
|
|
ae23c9 |
nbd/server.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++-----
|
|
|
ae23c9 |
nbd/trace-events | 1 +
|
|
|
ae23c9 |
3 files changed, 262 insertions(+), 25 deletions(-)
|
|
|
ae23c9 |
|
|
|
ae23c9 |
diff --git a/include/block/nbd.h b/include/block/nbd.h
|
|
|
ae23c9 |
index fcdcd54..8bb9606 100644
|
|
|
ae23c9 |
--- a/include/block/nbd.h
|
|
|
ae23c9 |
+++ b/include/block/nbd.h
|
|
|
ae23c9 |
@@ -229,11 +229,13 @@ enum {
|
|
|
ae23c9 |
#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1)
|
|
|
ae23c9 |
#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2)
|
|
|
ae23c9 |
|
|
|
ae23c9 |
-/* Flags for extents (NBDExtent.flags) of NBD_REPLY_TYPE_BLOCK_STATUS,
|
|
|
ae23c9 |
- * for base:allocation meta context */
|
|
|
ae23c9 |
+/* Extent flags for base:allocation in NBD_REPLY_TYPE_BLOCK_STATUS */
|
|
|
ae23c9 |
#define NBD_STATE_HOLE (1 << 0)
|
|
|
ae23c9 |
#define NBD_STATE_ZERO (1 << 1)
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+/* Extent flags for qemu:dirty-bitmap in NBD_REPLY_TYPE_BLOCK_STATUS */
|
|
|
ae23c9 |
+#define NBD_STATE_DIRTY (1 << 0)
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
static inline bool nbd_reply_type_is_error(int type)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
return type & (1 << 15);
|
|
|
ae23c9 |
@@ -315,6 +317,8 @@ void nbd_client_put(NBDClient *client);
|
|
|
ae23c9 |
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
|
|
ae23c9 |
Error **errp);
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+void nbd_export_bitmap(NBDExport *exp, const char *bitmap,
|
|
|
ae23c9 |
+ const char *bitmap_export_name, Error **errp);
|
|
|
ae23c9 |
|
|
|
ae23c9 |
/* nbd_read
|
|
|
ae23c9 |
* Reads @size bytes from @ioc. Returns 0 on success.
|
|
|
ae23c9 |
diff --git a/nbd/server.c b/nbd/server.c
|
|
|
ae23c9 |
index 9171cd4..2c2d62c 100644
|
|
|
ae23c9 |
--- a/nbd/server.c
|
|
|
ae23c9 |
+++ b/nbd/server.c
|
|
|
ae23c9 |
@@ -23,6 +23,13 @@
|
|
|
ae23c9 |
#include "nbd-internal.h"
|
|
|
ae23c9 |
|
|
|
ae23c9 |
#define NBD_META_ID_BASE_ALLOCATION 0
|
|
|
ae23c9 |
+#define NBD_META_ID_DIRTY_BITMAP 1
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+/* NBD_MAX_BITMAP_EXTENTS: 1 mb of extents data. An empirical
|
|
|
ae23c9 |
+ * constant. If an increase is needed, note that the NBD protocol
|
|
|
ae23c9 |
+ * recommends no larger than 32 mb, so that the client won't consider
|
|
|
ae23c9 |
+ * the reply as a denial of service attack. */
|
|
|
ae23c9 |
+#define NBD_MAX_BITMAP_EXTENTS (0x100000 / 8)
|
|
|
ae23c9 |
|
|
|
ae23c9 |
static int system_errno_to_nbd_errno(int err)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
@@ -80,6 +87,9 @@ struct NBDExport {
|
|
|
ae23c9 |
|
|
|
ae23c9 |
BlockBackend *eject_notifier_blk;
|
|
|
ae23c9 |
Notifier eject_notifier;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ BdrvDirtyBitmap *export_bitmap;
|
|
|
ae23c9 |
+ char *export_bitmap_context;
|
|
|
ae23c9 |
};
|
|
|
ae23c9 |
|
|
|
ae23c9 |
static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
|
|
|
ae23c9 |
@@ -92,6 +102,7 @@ typedef struct NBDExportMetaContexts {
|
|
|
ae23c9 |
bool valid; /* means that negotiation of the option finished without
|
|
|
ae23c9 |
errors */
|
|
|
ae23c9 |
bool base_allocation; /* export base:allocation context (block status) */
|
|
|
ae23c9 |
+ bool bitmap; /* export qemu:dirty-bitmap:<export bitmap name> */
|
|
|
ae23c9 |
} NBDExportMetaContexts;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
struct NBDClient {
|
|
|
ae23c9 |
@@ -814,6 +825,56 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta,
|
|
|
ae23c9 |
&meta->base_allocation, errp);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+/* nbd_meta_bitmap_query
|
|
|
ae23c9 |
+ *
|
|
|
ae23c9 |
+ * Handle query to 'qemu:' namespace.
|
|
|
ae23c9 |
+ * @len is the amount of text remaining to be read from the current name, after
|
|
|
ae23c9 |
+ * the 'qemu:' portion has been stripped.
|
|
|
ae23c9 |
+ *
|
|
|
ae23c9 |
+ * Return -errno on I/O error, 0 if option was completely handled by
|
|
|
ae23c9 |
+ * sending a reply about inconsistent lengths, or 1 on success. */
|
|
|
ae23c9 |
+static int nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta,
|
|
|
ae23c9 |
+ uint32_t len, Error **errp)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ bool dirty_bitmap = false;
|
|
|
ae23c9 |
+ size_t dirty_bitmap_len = strlen("dirty-bitmap:");
|
|
|
ae23c9 |
+ int ret;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (!meta->exp->export_bitmap) {
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_skip("no dirty-bitmap exported");
|
|
|
ae23c9 |
+ return nbd_opt_skip(client, len, errp);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (len == 0) {
|
|
|
ae23c9 |
+ if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
|
|
|
ae23c9 |
+ meta->bitmap = true;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_parse("empty");
|
|
|
ae23c9 |
+ return 1;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (len < dirty_bitmap_len) {
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:");
|
|
|
ae23c9 |
+ return nbd_opt_skip(client, len, errp);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ len -= dirty_bitmap_len;
|
|
|
ae23c9 |
+ ret = nbd_meta_pattern(client, "dirty-bitmap:", &dirty_bitmap, errp);
|
|
|
ae23c9 |
+ if (ret <= 0) {
|
|
|
ae23c9 |
+ return ret;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ if (!dirty_bitmap) {
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:");
|
|
|
ae23c9 |
+ return nbd_opt_skip(client, len, errp);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_parse("dirty-bitmap:");
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ return nbd_meta_empty_or_pattern(
|
|
|
ae23c9 |
+ client, meta->exp->export_bitmap_context +
|
|
|
ae23c9 |
+ strlen("qemu:dirty_bitmap:"), len, &meta->bitmap, errp);
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
/* nbd_negotiate_meta_query
|
|
|
ae23c9 |
*
|
|
|
ae23c9 |
* Parse namespace name and call corresponding function to parse body of the
|
|
|
ae23c9 |
@@ -829,9 +890,14 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta,
|
|
|
ae23c9 |
static int nbd_negotiate_meta_query(NBDClient *client,
|
|
|
ae23c9 |
NBDExportMetaContexts *meta, Error **errp)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
+ /*
|
|
|
ae23c9 |
+ * Both 'qemu' and 'base' namespaces have length = 5 including a
|
|
|
ae23c9 |
+ * colon. If another length namespace is later introduced, this
|
|
|
ae23c9 |
+ * should certainly be refactored.
|
|
|
ae23c9 |
+ */
|
|
|
ae23c9 |
int ret;
|
|
|
ae23c9 |
- char query[sizeof("base:") - 1];
|
|
|
ae23c9 |
- size_t baselen = strlen("base:");
|
|
|
ae23c9 |
+ size_t ns_len = 5;
|
|
|
ae23c9 |
+ char ns[5];
|
|
|
ae23c9 |
uint32_t len;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
ret = nbd_opt_read(client, &len, sizeof(len), errp);
|
|
|
ae23c9 |
@@ -840,25 +906,27 @@ static int nbd_negotiate_meta_query(NBDClient *client,
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
cpu_to_be32s(&len;;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- /* The only supported namespace for now is 'base'. So query should start
|
|
|
ae23c9 |
- * with 'base:'. Otherwise, we can ignore it and skip the remainder. */
|
|
|
ae23c9 |
- if (len < baselen) {
|
|
|
ae23c9 |
+ if (len < ns_len) {
|
|
|
ae23c9 |
trace_nbd_negotiate_meta_query_skip("length too short");
|
|
|
ae23c9 |
return nbd_opt_skip(client, len, errp);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- len -= baselen;
|
|
|
ae23c9 |
- ret = nbd_opt_read(client, query, baselen, errp);
|
|
|
ae23c9 |
+ len -= ns_len;
|
|
|
ae23c9 |
+ ret = nbd_opt_read(client, ns, ns_len, errp);
|
|
|
ae23c9 |
if (ret <= 0) {
|
|
|
ae23c9 |
return ret;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- if (strncmp(query, "base:", baselen) != 0) {
|
|
|
ae23c9 |
- trace_nbd_negotiate_meta_query_skip("not for base: namespace");
|
|
|
ae23c9 |
- return nbd_opt_skip(client, len, errp);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (!strncmp(ns, "base:", ns_len)) {
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_parse("base:");
|
|
|
ae23c9 |
+ return nbd_meta_base_query(client, meta, len, errp);
|
|
|
ae23c9 |
+ } else if (!strncmp(ns, "qemu:", ns_len)) {
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_parse("qemu:");
|
|
|
ae23c9 |
+ return nbd_meta_qemu_query(client, meta, len, errp);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- trace_nbd_negotiate_meta_query_parse("base:");
|
|
|
ae23c9 |
- return nbd_meta_base_query(client, meta, len, errp);
|
|
|
ae23c9 |
+ trace_nbd_negotiate_meta_query_skip("unknown namespace");
|
|
|
ae23c9 |
+ return nbd_opt_skip(client, len, errp);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
/* nbd_negotiate_meta_queries
|
|
|
ae23c9 |
@@ -928,6 +996,16 @@ static int nbd_negotiate_meta_queries(NBDClient *client,
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+ if (meta->bitmap) {
|
|
|
ae23c9 |
+ ret = nbd_negotiate_send_meta_context(client,
|
|
|
ae23c9 |
+ meta->exp->export_bitmap_context,
|
|
|
ae23c9 |
+ NBD_META_ID_DIRTY_BITMAP,
|
|
|
ae23c9 |
+ errp);
|
|
|
ae23c9 |
+ if (ret < 0) {
|
|
|
ae23c9 |
+ return ret;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
|
|
|
ae23c9 |
if (ret == 0) {
|
|
|
ae23c9 |
meta->valid = true;
|
|
|
ae23c9 |
@@ -1556,6 +1634,11 @@ void nbd_export_put(NBDExport *exp)
|
|
|
ae23c9 |
exp->blk = NULL;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+ if (exp->export_bitmap) {
|
|
|
ae23c9 |
+ bdrv_dirty_bitmap_set_qmp_locked(exp->export_bitmap, false);
|
|
|
ae23c9 |
+ g_free(exp->export_bitmap_context);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
g_free(exp);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
@@ -1797,9 +1880,15 @@ static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset,
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
/* nbd_co_send_extents
|
|
|
ae23c9 |
- * @extents should be in big-endian */
|
|
|
ae23c9 |
+ *
|
|
|
ae23c9 |
+ * @length is only for tracing purposes (and may be smaller or larger
|
|
|
ae23c9 |
+ * than the client's original request). @last controls whether
|
|
|
ae23c9 |
+ * NBD_REPLY_FLAG_DONE is sent. @extents should already be in
|
|
|
ae23c9 |
+ * big-endian format.
|
|
|
ae23c9 |
+ */
|
|
|
ae23c9 |
static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
|
|
ae23c9 |
- NBDExtent *extents, unsigned nb_extents,
|
|
|
ae23c9 |
+ NBDExtent *extents, unsigned int nb_extents,
|
|
|
ae23c9 |
+ uint64_t length, bool last,
|
|
|
ae23c9 |
uint32_t context_id, Error **errp)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
NBDStructuredMeta chunk;
|
|
|
ae23c9 |
@@ -1809,7 +1898,9 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
|
|
ae23c9 |
{.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])}
|
|
|
ae23c9 |
};
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS,
|
|
|
ae23c9 |
+ trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last);
|
|
|
ae23c9 |
+ set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0,
|
|
|
ae23c9 |
+ NBD_REPLY_TYPE_BLOCK_STATUS,
|
|
|
ae23c9 |
handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
|
|
|
ae23c9 |
stl_be_p(&chunk.context_id, context_id);
|
|
|
ae23c9 |
|
|
|
ae23c9 |
@@ -1819,8 +1910,8 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
|
|
ae23c9 |
/* Get block status from the exported device and send it to the client */
|
|
|
ae23c9 |
static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
|
|
|
ae23c9 |
BlockDriverState *bs, uint64_t offset,
|
|
|
ae23c9 |
- uint64_t length, uint32_t context_id,
|
|
|
ae23c9 |
- Error **errp)
|
|
|
ae23c9 |
+ uint64_t length, bool last,
|
|
|
ae23c9 |
+ uint32_t context_id, Error **errp)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
int ret;
|
|
|
ae23c9 |
NBDExtent extent;
|
|
|
ae23c9 |
@@ -1831,7 +1922,84 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
|
|
|
ae23c9 |
client, handle, -ret, "can't get block status", errp);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- return nbd_co_send_extents(client, handle, &extent, 1, context_id, errp);
|
|
|
ae23c9 |
+ return nbd_co_send_extents(client, handle, &extent, 1, length, last,
|
|
|
ae23c9 |
+ context_id, errp);
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+/*
|
|
|
ae23c9 |
+ * Populate @extents from a dirty bitmap. Unless @dont_fragment, the
|
|
|
ae23c9 |
+ * final extent may exceed the original @length. Store in @length the
|
|
|
ae23c9 |
+ * byte length encoded (which may be smaller or larger than the
|
|
|
ae23c9 |
+ * original), and return the number of extents used.
|
|
|
ae23c9 |
+ */
|
|
|
ae23c9 |
+static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
|
|
ae23c9 |
+ uint64_t *length, NBDExtent *extents,
|
|
|
ae23c9 |
+ unsigned int nb_extents,
|
|
|
ae23c9 |
+ bool dont_fragment)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ uint64_t begin = offset, end;
|
|
|
ae23c9 |
+ uint64_t overall_end = offset + *length;
|
|
|
ae23c9 |
+ unsigned int i = 0;
|
|
|
ae23c9 |
+ BdrvDirtyBitmapIter *it;
|
|
|
ae23c9 |
+ bool dirty;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ bdrv_dirty_bitmap_lock(bitmap);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ it = bdrv_dirty_iter_new(bitmap);
|
|
|
ae23c9 |
+ dirty = bdrv_get_dirty_locked(NULL, bitmap, offset);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ assert(begin < overall_end && nb_extents);
|
|
|
ae23c9 |
+ while (begin < overall_end && i < nb_extents) {
|
|
|
ae23c9 |
+ if (dirty) {
|
|
|
ae23c9 |
+ end = bdrv_dirty_bitmap_next_zero(bitmap, begin);
|
|
|
ae23c9 |
+ } else {
|
|
|
ae23c9 |
+ bdrv_set_dirty_iter(it, begin);
|
|
|
ae23c9 |
+ end = bdrv_dirty_iter_next(it);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ if (end == -1 || end - begin > UINT32_MAX) {
|
|
|
ae23c9 |
+ /* Cap to an aligned value < 4G beyond begin. */
|
|
|
ae23c9 |
+ end = MIN(bdrv_dirty_bitmap_size(bitmap),
|
|
|
ae23c9 |
+ begin + UINT32_MAX + 1 -
|
|
|
ae23c9 |
+ bdrv_dirty_bitmap_granularity(bitmap));
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ if (dont_fragment && end > overall_end) {
|
|
|
ae23c9 |
+ end = overall_end;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ extents[i].length = cpu_to_be32(end - begin);
|
|
|
ae23c9 |
+ extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0);
|
|
|
ae23c9 |
+ i++;
|
|
|
ae23c9 |
+ begin = end;
|
|
|
ae23c9 |
+ dirty = !dirty;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ bdrv_dirty_iter_free(it);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ bdrv_dirty_bitmap_unlock(bitmap);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ *length = end - offset;
|
|
|
ae23c9 |
+ return i;
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
|
|
|
ae23c9 |
+ BdrvDirtyBitmap *bitmap, uint64_t offset,
|
|
|
ae23c9 |
+ uint32_t length, bool dont_fragment, bool last,
|
|
|
ae23c9 |
+ uint32_t context_id, Error **errp)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ int ret;
|
|
|
ae23c9 |
+ unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS;
|
|
|
ae23c9 |
+ NBDExtent *extents = g_new(NBDExtent, nb_extents);
|
|
|
ae23c9 |
+ uint64_t final_length = length;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents,
|
|
|
ae23c9 |
+ nb_extents, dont_fragment);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ ret = nbd_co_send_extents(client, handle, extents, nb_extents,
|
|
|
ae23c9 |
+ final_length, last, context_id, errp);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ g_free(extents);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ return ret;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
/* nbd_co_receive_request
|
|
|
ae23c9 |
@@ -2051,11 +2219,34 @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
|
|
|
ae23c9 |
return nbd_send_generic_reply(client, request->handle, -EINVAL,
|
|
|
ae23c9 |
"need non-zero length", errp);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- if (client->export_meta.valid && client->export_meta.base_allocation) {
|
|
|
ae23c9 |
- return nbd_co_send_block_status(client, request->handle,
|
|
|
ae23c9 |
- blk_bs(exp->blk), request->from,
|
|
|
ae23c9 |
- request->len,
|
|
|
ae23c9 |
- NBD_META_ID_BASE_ALLOCATION, errp);
|
|
|
ae23c9 |
+ if (client->export_meta.valid &&
|
|
|
ae23c9 |
+ (client->export_meta.base_allocation ||
|
|
|
ae23c9 |
+ client->export_meta.bitmap))
|
|
|
ae23c9 |
+ {
|
|
|
ae23c9 |
+ if (client->export_meta.base_allocation) {
|
|
|
ae23c9 |
+ ret = nbd_co_send_block_status(client, request->handle,
|
|
|
ae23c9 |
+ blk_bs(exp->blk), request->from,
|
|
|
ae23c9 |
+ request->len,
|
|
|
ae23c9 |
+ !client->export_meta.bitmap,
|
|
|
ae23c9 |
+ NBD_META_ID_BASE_ALLOCATION,
|
|
|
ae23c9 |
+ errp);
|
|
|
ae23c9 |
+ if (ret < 0) {
|
|
|
ae23c9 |
+ return ret;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (client->export_meta.bitmap) {
|
|
|
ae23c9 |
+ ret = nbd_co_send_bitmap(client, request->handle,
|
|
|
ae23c9 |
+ client->exp->export_bitmap,
|
|
|
ae23c9 |
+ request->from, request->len,
|
|
|
ae23c9 |
+ request->flags & NBD_CMD_FLAG_REQ_ONE,
|
|
|
ae23c9 |
+ true, NBD_META_ID_DIRTY_BITMAP, errp);
|
|
|
ae23c9 |
+ if (ret < 0) {
|
|
|
ae23c9 |
+ return ret;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ return ret;
|
|
|
ae23c9 |
} else {
|
|
|
ae23c9 |
return nbd_send_generic_reply(client, request->handle, -EINVAL,
|
|
|
ae23c9 |
"CMD_BLOCK_STATUS not negotiated",
|
|
|
ae23c9 |
@@ -2207,3 +2398,44 @@ void nbd_client_new(NBDExport *exp,
|
|
|
ae23c9 |
co = qemu_coroutine_create(nbd_co_client_start, client);
|
|
|
ae23c9 |
qemu_coroutine_enter(co);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+void nbd_export_bitmap(NBDExport *exp, const char *bitmap,
|
|
|
ae23c9 |
+ const char *bitmap_export_name, Error **errp)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ BdrvDirtyBitmap *bm = NULL;
|
|
|
ae23c9 |
+ BlockDriverState *bs = blk_bs(exp->blk);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (exp->export_bitmap) {
|
|
|
ae23c9 |
+ error_setg(errp, "Export bitmap is already set");
|
|
|
ae23c9 |
+ return;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ while (true) {
|
|
|
ae23c9 |
+ bm = bdrv_find_dirty_bitmap(bs, bitmap);
|
|
|
ae23c9 |
+ if (bm != NULL || bs->backing == NULL) {
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ bs = bs->backing->bs;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (bm == NULL) {
|
|
|
ae23c9 |
+ error_setg(errp, "Bitmap '%s' is not found", bitmap);
|
|
|
ae23c9 |
+ return;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (bdrv_dirty_bitmap_enabled(bm)) {
|
|
|
ae23c9 |
+ error_setg(errp, "Bitmap '%s' is enabled", bitmap);
|
|
|
ae23c9 |
+ return;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (bdrv_dirty_bitmap_qmp_locked(bm)) {
|
|
|
ae23c9 |
+ error_setg(errp, "Bitmap '%s' is locked", bitmap);
|
|
|
ae23c9 |
+ return;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ bdrv_dirty_bitmap_set_qmp_locked(bm, true);
|
|
|
ae23c9 |
+ exp->export_bitmap = bm;
|
|
|
ae23c9 |
+ exp->export_bitmap_context =
|
|
|
ae23c9 |
+ g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name);
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
diff --git a/nbd/trace-events b/nbd/trace-events
|
|
|
ae23c9 |
index dee081e..5e1d4af 100644
|
|
|
ae23c9 |
--- a/nbd/trace-events
|
|
|
ae23c9 |
+++ b/nbd/trace-events
|
|
|
ae23c9 |
@@ -64,6 +64,7 @@ nbd_co_send_simple_reply(uint64_t handle, uint32_t error, const char *errname, i
|
|
|
ae23c9 |
nbd_co_send_structured_done(uint64_t handle) "Send structured reply done: handle = %" PRIu64
|
|
|
ae23c9 |
nbd_co_send_structured_read(uint64_t handle, uint64_t offset, void *data, size_t size) "Send structured read data reply: handle = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu"
|
|
|
ae23c9 |
nbd_co_send_structured_read_hole(uint64_t handle, uint64_t offset, size_t size) "Send structured read hole reply: handle = %" PRIu64 ", offset = %" PRIu64 ", len = %zu"
|
|
|
ae23c9 |
+nbd_co_send_extents(uint64_t handle, unsigned int extents, uint32_t id, uint64_t length, int last) "Send block status reply: handle = %" PRIu64 ", extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)"
|
|
|
ae23c9 |
nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'"
|
|
|
ae23c9 |
nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)"
|
|
|
ae23c9 |
nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32
|
|
|
ae23c9 |
--
|
|
|
ae23c9 |
1.8.3.1
|
|
|
ae23c9 |
|