qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Eric Blake <eblake@redhat.com>
To: qemu-devel@nongnu.org
Cc: qemu-block@nongnu.org, alex@alex.org.uk,
	Paolo Bonzini <pbonzini@redhat.com>,
	Kevin Wolf <kwolf@redhat.com>, Max Reitz <mreitz@redhat.com>
Subject: [Qemu-devel] [PATCH v3 32/44] nbd: Share common option-sending code in client
Date: Fri, 22 Apr 2016 17:40:40 -0600	[thread overview]
Message-ID: <1461368452-10389-33-git-send-email-eblake@redhat.com> (raw)
In-Reply-To: <1461368452-10389-1-git-send-email-eblake@redhat.com>

Rather than open-coding each option request, it's easier to
have common helper functions do the work.  That in turn requires
having convenient packed types for handling option requests
and replies.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Alex Bligh <alex@alex.org.uk>

---
v3: rebase, tweak a debug message
---
 include/block/nbd.h |  29 +++++-
 nbd/nbd-internal.h  |   2 +-
 nbd/client.c        | 250 ++++++++++++++++++++++------------------------------
 3 files changed, 129 insertions(+), 152 deletions(-)

diff --git a/include/block/nbd.h b/include/block/nbd.h
index f4ae86c..5227ec6 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -26,20 +26,41 @@
 #include "io/channel-socket.h"
 #include "crypto/tlscreds.h"

+/* Handshake phase structs */
+
+struct nbd_option {
+    uint64_t magic; /* NBD_OPTS_MAGIC */
+    uint32_t option; /* NBD_OPT_* */
+    uint32_t length;
+} QEMU_PACKED;
+typedef struct nbd_option nbd_option;
+
+struct nbd_opt_reply {
+    uint64_t magic; /* NBD_REP_MAGIC */
+    uint32_t option; /* NBD_OPT_* */
+    uint32_t type; /* NBD_REP_* */
+    uint32_t length;
+} QEMU_PACKED;
+typedef struct nbd_opt_reply nbd_opt_reply;
+
+/* Transmission phase structs */
+
 struct nbd_request {
-    uint32_t magic;
-    uint16_t flags;
-    uint16_t type;
+    uint32_t magic; /* NBD_REQUEST_MAGIC */
+    uint16_t flags; /* NBD_CMD_FLAG_* */
+    uint16_t type; /* NBD_CMD_* */
     uint64_t handle;
     uint64_t from;
     uint32_t len;
 } QEMU_PACKED;
+typedef struct nbd_request nbd_request;

 struct nbd_reply {
-    uint32_t magic;
+    uint32_t magic; /* NBD_REPLY_MAGIC */
     uint32_t error;
     uint64_t handle;
 } QEMU_PACKED;
+typedef struct nbd_reply nbd_reply;

 /* Transmission (export) flags: sent from server to client during handshake,
    but describe what will happen during transmission */
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index d0108a1..95069db 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -61,7 +61,7 @@
 #define NBD_REPLY_MAGIC         0x67446698
 #define NBD_OPTS_MAGIC          0x49484156454F5054LL
 #define NBD_CLIENT_MAGIC        0x0000420281861253LL
-#define NBD_REP_MAGIC           0x3e889045565a9LL
+#define NBD_REP_MAGIC           0x0003e889045565a9LL

 #define NBD_SET_SOCK            _IO(0xab, 0)
 #define NBD_SET_BLKSIZE         _IO(0xab, 1)
diff --git a/nbd/client.c b/nbd/client.c
index a9e173a..6626fa8 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -75,64 +75,123 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);

 */

+/* Send an option request. Return 0 if successful, -1 with errp set if
+ * it is impossible to continue. */
+static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt,
+                                   uint32_t len, const char *data,
+                                   Error **errp)
+{
+    nbd_option req;
+    QEMU_BUILD_BUG_ON(sizeof(req) != 16);

-/* If type represents success, return 1 without further action.
- * If type represents an error reply, consume the rest of the packet on ioc.
- * Then return 0 for unsupported (so the client can fall back to
- * other approaches), or -1 with errp set for other errors.
+    if (len == -1) {
+        req.length = len = strlen(data);
+    }
+    TRACE("Sending option request %"PRIu32", len %"PRIu32, opt, len);
+
+    stq_be_p(&req.magic, NBD_OPTS_MAGIC);
+    stl_be_p(&req.option, opt);
+    stl_be_p(&req.length, len);
+
+    if (write_sync(ioc, &req, sizeof(req)) != sizeof(req)) {
+        error_setg(errp, "Failed to send option request header");
+        return -1;
+    }
+
+    if (len && write_sync(ioc, (char *) data, len) != len) {
+        error_setg(errp, "Failed to send option request data");
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Receive the header of an option reply, which should match the given
+ * opt.  Read through the length field, but NOT the length bytes of
+ * payload. Return 0 if successful, -1 with errp set if it is
+ * impossible to continue. */
+static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
+                                    nbd_opt_reply *reply, Error **errp)
+{
+    QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
+    if (read_sync(ioc, reply, sizeof(*reply)) != sizeof(*reply)) {
+        error_setg(errp, "failed to read option reply");
+        return -1;
+    }
+    be64_to_cpus(&reply->magic);
+    be32_to_cpus(&reply->option);
+    be32_to_cpus(&reply->type);
+    be32_to_cpus(&reply->length);
+
+    TRACE("Received option reply %"PRIx32", type %"PRIx32", len %"PRIu32,
+          reply->option, reply->type, reply->length);
+
+    if (reply->magic != NBD_REP_MAGIC) {
+        error_setg(errp, "Unexpected option reply magic");
+        return -1;
+    }
+    if (reply->option != opt) {
+        error_setg(errp, "Unexpected option type %x expected %x",
+                   reply->option, opt);
+        return -1;
+    }
+    return 0;
+}
+
+/* If reply represents success, return 1 without further action.
+ * If reply represents an error, consume the optional payload of
+ * the packet on ioc.  Then return 0 for unsupported (so the client
+ * can fall back to other approaches), or -1 with errp set for other
+ * errors.
  */
-static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type,
+static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
                                 Error **errp)
 {
-    uint32_t len;
     char *msg = NULL;
     int result = -1;

-    if (!(type & (1 << 31))) {
+    if (!(reply->type & (1 << 31))) {
         return 1;
     }

-    if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
-        error_setg(errp, "failed to read option length");
-        return -1;
-    }
-    len = be32_to_cpu(len);
-    if (len) {
-        if (len > NBD_MAX_BUFFER_SIZE) {
+    if (reply->length) {
+        if (reply->length > NBD_MAX_BUFFER_SIZE) {
             error_setg(errp, "server's error message is too long");
             goto cleanup;
         }
-        msg = g_malloc(len + 1);
-        if (read_sync(ioc, msg, len) != len) {
+        msg = g_malloc(reply->length + 1);
+        if (read_sync(ioc, msg, reply->length) != reply->length) {
             error_setg(errp, "failed to read option error message");
             goto cleanup;
         }
-        msg[len] = '\0';
+        msg[reply->length] = '\0';
     }

-    switch (type) {
+    switch (reply->type) {
     case NBD_REP_ERR_UNSUP:
         TRACE("server doesn't understand request %" PRIx32
-              ", attempting fallback", opt);
+              ", attempting fallback", reply->option);
         result = 0;
         goto cleanup;

     case NBD_REP_ERR_POLICY:
-        error_setg(errp, "Denied by server for option %" PRIx32, opt);
+        error_setg(errp, "Denied by server for option %" PRIx32,
+                   reply->option);
         break;

     case NBD_REP_ERR_INVALID:
-        error_setg(errp, "Invalid data length for option %" PRIx32, opt);
+        error_setg(errp, "Invalid data length for option %" PRIx32,
+                   reply->option);
         break;

     case NBD_REP_ERR_TLS_REQD:
         error_setg(errp, "TLS negotiation required before option %" PRIx32,
-                   opt);
+                   reply->option);
         break;

     default:
         error_setg(errp, "Unknown error code when asking for option %" PRIx32,
-                   opt);
+                   reply->option);
         break;
     }

@@ -147,58 +206,29 @@ static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type,

 static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
 {
-    uint64_t magic;
-    uint32_t opt;
-    uint32_t type;
+    nbd_opt_reply reply;
     uint32_t len;
     uint32_t namelen;
     int error;

     *name = NULL;
-    if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
-        error_setg(errp, "failed to read list option magic");
+    if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) {
         return -1;
     }
-    magic = be64_to_cpu(magic);
-    if (magic != NBD_REP_MAGIC) {
-        error_setg(errp, "Unexpected option list magic");
-        return -1;
-    }
-    if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
-        error_setg(errp, "failed to read list option");
-        return -1;
-    }
-    opt = be32_to_cpu(opt);
-    if (opt != NBD_OPT_LIST) {
-        error_setg(errp, "Unexpected option type %" PRIx32 " expected %x",
-                   opt, NBD_OPT_LIST);
-        return -1;
-    }
-
-    if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
-        error_setg(errp, "failed to read list option type");
-        return -1;
-    }
-    type = be32_to_cpu(type);
-    error = nbd_handle_reply_err(ioc, opt, type, errp);
+    error = nbd_handle_reply_err(ioc, &reply, errp);
     if (error <= 0) {
         return error;
     }
+    len = reply.length;

-    if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
-        error_setg(errp, "failed to read option length");
-        return -1;
-    }
-    len = be32_to_cpu(len);
-
-    if (type == NBD_REP_ACK) {
+    if (reply.type == NBD_REP_ACK) {
         if (len != 0) {
             error_setg(errp, "length too long for option end");
             return -1;
         }
-    } else if (type == NBD_REP_SERVER) {
+    } else if (reply.type == NBD_REP_SERVER) {
         if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) {
-            error_setg(errp, "incorrect option length");
+            error_setg(errp, "incorrect option length %"PRIu32, len);
             return -1;
         }
         if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
@@ -240,7 +270,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
         }
     } else {
         error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
-                   type, NBD_REP_SERVER);
+                   reply.type, NBD_REP_SERVER);
         return -1;
     }
     return 1;
@@ -251,24 +281,10 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
                                      const char *wantname,
                                      Error **errp)
 {
-    uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
-    uint32_t opt = cpu_to_be32(NBD_OPT_LIST);
-    uint32_t length = 0;
     bool foundExport = false;

     TRACE("Querying export list");
-    if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
-        error_setg(errp, "Failed to send list option magic");
-        return -1;
-    }
-
-    if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
-        error_setg(errp, "Failed to send list option number");
-        return -1;
-    }
-
-    if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
-        error_setg(errp, "Failed to send list option length");
+    if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
         return -1;
     }

@@ -314,72 +330,29 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
                                         QCryptoTLSCreds *tlscreds,
                                         const char *hostname, Error **errp)
 {
-    uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
-    uint32_t opt = cpu_to_be32(NBD_OPT_STARTTLS);
-    uint32_t length = 0;
-    uint32_t type;
+    nbd_opt_reply reply;
     QIOChannelTLS *tioc;
     struct NBDTLSHandshakeData data = { 0 };

     TRACE("Requesting TLS from server");
-    if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
-        error_setg(errp, "Failed to send option magic");
-        return NULL;
-    }
-
-    if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
-        error_setg(errp, "Failed to send option number");
-        return NULL;
-    }
-
-    if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
-        error_setg(errp, "Failed to send option length");
-        return NULL;
-    }
-
-    TRACE("Getting TLS reply from server1");
-    if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
-        error_setg(errp, "failed to read option magic");
-        return NULL;
-    }
-    magic = be64_to_cpu(magic);
-    if (magic != NBD_REP_MAGIC) {
-        error_setg(errp, "Unexpected option magic");
-        return NULL;
-    }
-    TRACE("Getting TLS reply from server2");
-    if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
-        error_setg(errp, "failed to read option");
-        return NULL;
-    }
-    opt = be32_to_cpu(opt);
-    if (opt != NBD_OPT_STARTTLS) {
-        error_setg(errp, "Unexpected option type %" PRIx32 " expected %x",
-                   opt, NBD_OPT_STARTTLS);
+    if (nbd_send_option_request(ioc, NBD_OPT_STARTTLS, 0, NULL, errp) < 0) {
         return NULL;
     }

     TRACE("Getting TLS reply from server");
-    if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
-        error_setg(errp, "failed to read option type");
+    if (nbd_receive_option_reply(ioc, NBD_OPT_STARTTLS, &reply, errp) < 0) {
         return NULL;
     }
-    type = be32_to_cpu(type);
-    if (type != NBD_REP_ACK) {
+
+    if (reply.type != NBD_REP_ACK) {
         error_setg(errp, "Server rejected request to start TLS %" PRIx32,
-                   type);
+                   reply.type);
         return NULL;
     }

-    TRACE("Getting TLS reply from server");
-    if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
-        error_setg(errp, "failed to read option length");
-        return NULL;
-    }
-    length = be32_to_cpu(length);
-    if (length != 0) {
+    if (reply.length != 0) {
         error_setg(errp, "Start TLS response was not zero %" PRIu32,
-                   length);
+                   reply.length);
         return NULL;
     }

@@ -466,8 +439,6 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,

     if (magic == NBD_OPTS_MAGIC) {
         uint32_t clientflags = 0;
-        uint32_t opt;
-        uint32_t namesize;
         uint16_t globalflags;
         bool fixedNewStyle = false;

@@ -517,28 +488,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
                 goto fail;
             }
         }
-        /* write the export name */
-        magic = cpu_to_be64(magic);
-        if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
-            error_setg(errp, "Failed to send export name magic");
-            goto fail;
-        }
-        opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
-        if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
-            error_setg(errp, "Failed to send export name option number");
-            goto fail;
-        }
-        namesize = cpu_to_be32(strlen(name));
-        if (write_sync(ioc, &namesize, sizeof(namesize)) !=
-            sizeof(namesize)) {
-            error_setg(errp, "Failed to send export name length");
-            goto fail;
-        }
-        if (write_sync(ioc, (char *)name, strlen(name)) != strlen(name)) {
-            error_setg(errp, "Failed to send export name");
+        /* write the export name request */
+        if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, name,
+                                    errp) < 0) {
             goto fail;
         }

+        /* Read the response */
         if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
             error_setg(errp, "Failed to read export length");
             goto fail;
-- 
2.5.5

  parent reply	other threads:[~2016-04-22 23:41 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-22 23:40 [Qemu-devel] [PATCH v3 00/44] NBD protocol additions Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 01/44] nbd: More debug typo fixes, use correct formats Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 02/44] nbd: Quit server after any write error Eric Blake
2016-04-25  9:21   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 03/44] nbd: Improve server handling of bogus commands Eric Blake
2016-04-25  9:29   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 04/44] nbd: Reject unknown request flags Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 05/44] nbd: Group all Linux-specific ioctl code in one place Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 06/44] nbd: Clean up ioctl handling of qemu-nbd -c Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 07/44] nbd: Limit nbdflags to 16 bits Eric Blake
2016-04-25  9:24   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 08/44] nbd: Add qemu-nbd -D for human-readable description Eric Blake
2016-04-25  9:25   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 09/44] block: Allow BDRV_REQ_FUA through blk_pwrite() Eric Blake
2016-04-23  8:12   ` Denis V. Lunev
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 10/44] fdc: Switch to byte-based block access Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 11/44] nand: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 12/44] onenand: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 13/44] pflash: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 14/44] sd: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 15/44] m25p80: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 16/44] atapi: " Eric Blake
2016-04-25 21:36   ` John Snow
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 17/44] nbd: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 18/44] qemu-img: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 19/44] qemu-io: " Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 20/44] block: Switch blk_read_unthrottled() to byte interface Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 21/44] block: Switch blk_write_zeroes() " Eric Blake
2016-04-23  8:12   ` Denis V. Lunev
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 22/44] block: Kill blk_write(), blk_read() Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 23/44] qemu-io: Add missing option documentation Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 24/44] qemu-io: Add 'write -f' to test FUA flag Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 25/44] qemu-io: Add 'open -u' to set BDRV_O_UNMAP after the fact Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 26/44] qemu-io: Add 'write -z -u' to test MAY_UNMAP flag Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 27/44] nbd: Use BDRV_REQ_FUA for better FUA where supported Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 28/44] nbd: Detect servers that send unexpected error values Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 29/44] nbd: Avoid magic number for NBD max name size Eric Blake
2016-04-25  9:32   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 30/44] nbd: Treat flags vs. command type as separate fields Eric Blake
2016-04-25  9:34   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 31/44] nbd: Share common reply-sending code in server Eric Blake
2016-04-25  9:34   ` Alex Bligh
2016-04-22 23:40 ` Eric Blake [this message]
2016-04-25  9:37   ` [Qemu-devel] [PATCH v3 32/44] nbd: Share common option-sending code in client Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 33/44] nbd: Let client skip portions of server reply Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 34/44] nbd: Less allocation during NBD_OPT_LIST Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 35/44] nbd: Support shorter handshake Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 36/44] nbd: Improve handling of shutdown requests Eric Blake
2016-04-25  9:47   ` Alex Bligh
2016-04-25 19:20     ` Eric Blake
2016-04-25 19:40       ` Alex Bligh
2016-04-25 19:48         ` Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 37/44] nbd: Create struct for tracking export info Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 38/44] block: Add blk_get_opt_transfer_length() Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 39/44] nbd: Implement NBD_OPT_GO on server Eric Blake
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 40/44] nbd: Implement NBD_OPT_GO on client Eric Blake
2016-04-25 10:31   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 41/44] nbd: Implement NBD_CMD_WRITE_ZEROES on server Eric Blake
2016-04-23  9:00   ` Pavel Borzenkov
2016-04-25 12:11   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 42/44] nbd: Implement NBD_CMD_WRITE_ZEROES on client Eric Blake
2016-04-25 12:12   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 43/44] nbd: Implement NBD_OPT_BLOCK_SIZE on server Eric Blake
2016-04-25 12:16   ` Alex Bligh
2016-04-22 23:40 ` [Qemu-devel] [PATCH v3 44/44] nbd: Implement NBD_OPT_BLOCK_SIZE on client Eric Blake
2016-04-25 12:19   ` Alex Bligh
2016-04-25 19:16     ` Eric Blake

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1461368452-10389-33-git-send-email-eblake@redhat.com \
    --to=eblake@redhat.com \
    --cc=alex@alex.org.uk \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).