diff --git a/qapi/block-core.json b/qapi/block-core.json
index cc3ede06309..be0456f72b7 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3484,12 +3484,22 @@
 #
 # @tls-creds:   TLS credentials ID
 #
+# @x-block-status: A string containing a semicolon-separated list of
+#                  block status context names to query and then
+#                  request (see NBD_OPT_LIST_META_CONTEXT in the NBD
+#                  protocol), only useful for debugging server
+#                  behavior. If omitted, no query is made, and the
+#                  request uses just "base:allocation". Since this
+#                  command is already a hack, it uses a flat string
+#                  instead of ['str']. (since 3.0)
+#
 # Since: 2.9
 ##
 { 'struct': 'BlockdevOptionsNbd',
   'data': { 'server': 'SocketAddress',
             '*export': 'str',
-            '*tls-creds': 'str' } }
+            '*tls-creds': 'str',
+            '*x-block-status': 'str' } }

 ##
 # @BlockdevOptionsRaw:
diff --git a/block/nbd-client.h b/block/nbd-client.h
index 0ece76e5aff..18405e84a50 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -45,6 +45,7 @@ int nbd_client_init(BlockDriverState *bs,
                     const char *export_name,
                     QCryptoTLSCreds *tlscreds,
                     const char *hostname,
+                    const char *x_block_status,
                     Error **errp);
 void nbd_client_close(BlockDriverState *bs);

diff --git a/include/block/nbd.h b/include/block/nbd.h
index 8bb9606c39b..79e237e15fa 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -258,6 +258,7 @@ static inline bool nbd_reply_type_is_error(int type)
 struct NBDExportInfo {
     /* Set by client before nbd_receive_negotiate() */
     bool request_sizes;
+    const char *x_block_status;

     /* In-out fields, set by client before nbd_receive_negotiate() and
      * updated by server results during nbd_receive_negotiate() */
diff --git a/block/nbd-client.c b/block/nbd-client.c
index 8d69eaaa32f..d27d65d6519 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -970,6 +970,7 @@ int nbd_client_init(BlockDriverState *bs,
                     const char *export,
                     QCryptoTLSCreds *tlscreds,
                     const char *hostname,
+                    const char *x_block_status,
                     Error **errp)
 {
     NBDClientSession *client = nbd_get_client_session(bs);
@@ -982,6 +983,7 @@ int nbd_client_init(BlockDriverState *bs,
     client->info.request_sizes = true;
     client->info.structured_reply = true;
     client->info.base_allocation = true;
+    client->info.x_block_status = x_block_status;
     ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
                                 tlscreds, hostname,
                                 &client->ioc, &client->info, errp);
diff --git a/block/nbd.c b/block/nbd.c
index 13db4030e67..019d8e05450 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -378,6 +378,11 @@ static QemuOptsList nbd_runtime_opts = {
             .type = QEMU_OPT_STRING,
             .help = "ID of the TLS credentials to use",
         },
+        {
+            .name = "x-block-status",
+            .type = QEMU_OPT_STRING,
+            .help = "debugging only: block status contexts to request",
+        },
         { /* end of list */ }
     },
 };
@@ -438,8 +443,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
     }

     /* NBD handshake */
-    ret = nbd_client_init(bs, sioc, s->export,
-                          tlscreds, hostname, errp);
+    ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname,
+                          qemu_opt_get(opts, "x-block-status"), errp);
  error:
     if (sioc) {
         object_unref(OBJECT(sioc));
diff --git a/nbd/client.c b/nbd/client.c
index 232ff4f46da..f80d88f6c1e 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -601,6 +601,119 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
     return QIO_CHANNEL(tioc);
 }

+/* nbd_negotiate_list_meta_context:
+ * Hack for testing meta context negotiation. Subdivide list on semicolons,
+ * then pass that many queries for info and trace the results.
+ * return 0 for successful negotiation
+ *        -1 with errp set for any other error
+ */
+static int nbd_negotiate_list_meta_context(QIOChannel *ioc,
+                                           const char *export,
+                                           const char *context,
+                                           Error **errp)
+{
+    int ret;
+    NBDOptionReply reply;
+    uint32_t received_id = 0;
+    char **list = g_strsplit(context, ";", -1);
+    char **iter;
+    uint32_t export_len = strlen(export);
+    uint32_t context_len;
+    uint32_t queries = g_strv_length(list);
+    uint32_t data_len = sizeof(export_len) + export_len +
+        sizeof(queries) + sizeof(context_len) * queries;
+    /* Slight overallocation of data is okay */
+    char *data = g_malloc(data_len + strlen(context));
+    char *p = data;
+
+    trace_nbd_opt_meta_request("list", context, export);
+    stl_be_p(p, export_len);
+    memcpy(p += sizeof(export_len), export, export_len);
+    stl_be_p(p += export_len, queries);
+    p += sizeof(queries);
+    for (iter = list; *iter; iter++) {
+        context_len = strlen(*iter);
+        stl_be_p(p, context_len);
+        memcpy(p += sizeof(context_len), *iter, context_len);
+        data_len += context_len;
+        p += context_len;
+    }
+
+    ret = nbd_send_option_request(ioc, NBD_OPT_LIST_META_CONTEXT, data_len,
+                                  data, errp);
+    g_free(data);
+    if (ret < 0) {
+        goto out;
+    }
+
+    if (nbd_receive_option_reply(ioc, NBD_OPT_LIST_META_CONTEXT, &reply,
+                                 errp) < 0)
+    {
+        ret = -1;
+        goto out;
+    }
+
+    ret = nbd_handle_reply_err(ioc, &reply, errp);
+    if (ret <= 0) {
+        goto out;
+    }
+
+    while (reply.type == NBD_REP_META_CONTEXT) {
+        char *name;
+
+        if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) {
+            ret = -1;
+            goto out;
+        }
+        be32_to_cpus(&received_id);
+
+        reply.length -= sizeof(received_id);
+        name = g_malloc(reply.length + 1);
+        if (nbd_read(ioc, name, reply.length, errp) < 0) {
+            g_free(name);
+            ret = -1;
+            goto out;
+        }
+        name[reply.length] = '\0';
+
+        trace_nbd_opt_meta_reply(name, received_id);
+        g_free(name);
+
+        /* read next part of reply */
+        if (nbd_receive_option_reply(ioc, NBD_OPT_LIST_META_CONTEXT, &reply,
+                                     errp) < 0)
+        {
+            ret = -1;
+            goto out;
+        }
+
+        ret = nbd_handle_reply_err(ioc, &reply, errp);
+        if (ret <= 0) {
+            goto out;
+        }
+    }
+
+    if (reply.type != NBD_REP_ACK) {
+        error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
+                   reply.type, NBD_REP_ACK);
+        nbd_send_opt_abort(ioc);
+        ret = -1;
+        goto out;
+    }
+    if (reply.length) {
+        error_setg(errp, "Unexpected length to ACK response");
+        nbd_send_opt_abort(ioc);
+        ret = -1;
+        goto out;
+    }
+
+    ret = 0;
+
+ out:
+    g_strfreev(list);
+    return ret;
+}
+
 /* nbd_negotiate_simple_meta_context:
  * Set one meta context. Simple means that reply must contain zero (not
  * negotiated) or one (negotiated) contexts. More contexts would be considered
@@ -622,14 +735,14 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
     uint32_t received_id = 0;
     bool received = false;
     uint32_t export_len = strlen(export);
-    uint32_t context_len = strlen(context);
+    uint32_t context_len = strchrnul(context, ';') - context;
     uint32_t data_len = sizeof(export_len) + export_len +
                         sizeof(uint32_t) + /* number of queries */
                         sizeof(context_len) + context_len;
     char *data = g_malloc(data_len);
     char *p = data;

-    trace_nbd_opt_meta_request(context, export);
+    trace_nbd_opt_meta_request("set", context, export);
     stl_be_p(p, export_len);
     memcpy(p += sizeof(export_len), export, export_len);
     stl_be_p(p += export_len, 1);
@@ -677,7 +790,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
             return -1;
         }
         name[reply.length] = '\0';
-        if (strcmp(context, name)) {
+        if (strncmp(context, name, context_len)) {
             error_setg(errp, "Failed to negotiate meta context '%s', server "
                        "answered with different context '%s'", context,
                        name);
@@ -829,9 +942,18 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
                 info->structured_reply = result == 1;
             }

+            if (info->structured_reply && info->x_block_status &&
+                nbd_negotiate_list_meta_context(ioc, name,
+                                                info->x_block_status,
+                                                errp) < 0) {
+                goto fail;
+            }
             if (info->structured_reply && base_allocation) {
+                if (!info->x_block_status) {
+                    info->x_block_status = "base:allocation";
+                }
                 result = nbd_negotiate_simple_meta_context(
-                        ioc, name, "base:allocation",
+                        ioc, name, info->x_block_status,
                         &info->meta_base_allocation_id, errp);
                 if (result < 0) {
                     goto fail;
diff --git a/nbd/trace-events b/nbd/trace-events
index 5e1d4afe8e6..7b587a64856 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -10,7 +10,7 @@ nbd_receive_query_exports_start(const char *wantname) "Querying export list for
 nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'"
 nbd_receive_starttls_new_client(void) "Setting up TLS"
 nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake"
-nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s"
+nbd_opt_meta_request(const char *act, const char *context, const char *export) "Requesting to %s meta context %s for export %s"
 nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32
 nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
 nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64
