This is a very basic implementation which could do with optimisation.

Signed-off-by: Alex Bligh <[email protected]>
---
 nbd-server.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 nbd.h        |  5 +++-
 2 files changed, 87 insertions(+), 6 deletions(-)

This is marked PATCH/RFC as it's only compile tested as I don't yet have
a client that interoperates with it.

Changes from v1:

* Add support for NBD_CMD_FLAG_NO_HOLE

diff --git a/nbd-server.c b/nbd-server.c
index 4edb883..1b3bb48 100644
--- a/nbd-server.c
+++ b/nbd-server.c
@@ -261,6 +261,8 @@ static inline const char * getcommandname(uint64_t command) 
{
                return "NBD_CMD_FLUSH";
        case NBD_CMD_TRIM:
                return "NBD_CMD_TRIM";
+       case NBD_CMD_WRITE_ZEROES:
+               return "NBD_CMD_WRITE_ZEROES";
        default:
                return "UNKNOWN";
        }
@@ -1183,6 +1185,39 @@ int expwrite(off_t a, char *buf, size_t len, CLIENT 
*client, int fua) {
        return 0;
 }
 
+
+/**
+ * Write an amount of zeroes at a given offset to the right file.
+ * This routine could be optimised by not calling expwrite. However,
+ * this is by far the simplest way to do it.
+ *
+ * @param req the request
+ * @param client The client we're going to write for.
+ * @return 0 on success, nonzero on failure
+ **/
+int expwrite_zeroes(struct nbd_request* req, CLIENT* client) {
+       off_t a = req->from;
+       size_t len = req->len;
+       int fua = !!(req->type & NBD_CMD_FLAG_FUA);
+       size_t maxsize = 64LL*1024LL*1024LL;
+       /* use calloc() as sadly MAP_ANON is apparently not POSIX standard */
+       char *buf = calloc (1, maxsize);
+       int ret;
+       while (len > 0) {
+               size_t l = len;
+               if (l > maxsize)
+                       l = maxsize;
+               ret = expwrite(a, buf, l, client, fua);
+               if (ret) {
+                       free(buf);
+                       return ret;
+               }
+               len -= l;
+       }
+       free(buf);
+       return 0;
+}
+
 /**
  * Flush data to a client
  *
@@ -1382,7 +1417,7 @@ CLIENT* negotiate(int net, GArray* servers) {
 
 void send_export_info(CLIENT* client) {
        uint64_t size_host = htonll((u64)(client->exportsize));
-       uint16_t flags = NBD_FLAG_HAS_FLAGS;
+       uint16_t flags = NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_WRITE_ZEROES;
 
        if (write(client->net, &size_host, 8) < 0)
                err("Negotiation failed/9: %m");
@@ -1481,7 +1516,7 @@ static void handle_write(CLIENT* client, struct 
nbd_request* req, void* data) {
                DEBUG("[WRITE to READONLY!]");
                rep.error = nbd_errno(EPERM);
        } else {
-               if(expwrite(req->from, data, req->len, client, (req->type 
&~NBD_CMD_MASK_COMMAND))) {
+               if(expwrite(req->from, data, req->len, client, !!(req->type & 
NBD_CMD_FLAG_FUA))) {
                        DEBUG("Write failed: %m");
                        rep.error = nbd_errno(errno);
                }
@@ -1517,13 +1552,38 @@ static void handle_trim(CLIENT* client, struct 
nbd_request* req) {
        pthread_mutex_unlock(&(client->lock));
 }
 
+static void handle_write_zeroes(CLIENT* client, struct nbd_request* req) {
+       struct nbd_reply rep;
+       DEBUG("handling write_zeroes request\n");
+       setup_reply(&rep, req);
+       if(expwrite_zeroes(req, client)) {
+               DEBUG("Write_zeroes failed: %m");
+               rep.error = nbd_errno(errno);
+       } else {
+               // if the device supports trim, and we are not told we cannot
+               // make a hole, then make a hole. Note that this hole-making
+               // may fail silently, and may not be complete (depending on
+               // alignment etc.) which is why we need to do the zero write
+               // first (sadly).
+               if ((client->server->flags & F_TRIM) && !(req->type & 
NBD_CMD_FLAG_NO_HOLE)) {
+                       if (exptrim(req, client)) {
+                               DEBUG("Trim after write_zeroes failed: %m");
+                               rep.error = nbd_errno(errno);
+                       }
+               }
+       }
+       pthread_mutex_lock(&(client->lock));
+       writeit(client->net, &rep, sizeof rep);
+       pthread_mutex_unlock(&(client->lock));
+}
+
 static void handle_request(gpointer data, gpointer user_data) {
        struct work_package* package = (struct work_package*) data;
        uint32_t type = package->req->type & NBD_CMD_MASK_COMMAND;
        uint32_t flags = package->req->type & ~NBD_CMD_MASK_COMMAND;
        struct nbd_reply rep;
 
-       if(flags & ~NBD_CMD_FLAG_FUA) {
+       if(flags & ~(NBD_CMD_FLAG_FUA | NBD_CMD_FLAG_NO_HOLE)) {
                msg(LOG_ERR, "E: received invalid flag %d on command %d, 
ignoring", flags, type);
                goto error;
        }
@@ -1541,6 +1601,9 @@ static void handle_request(gpointer data, gpointer 
user_data) {
                case NBD_CMD_TRIM:
                        handle_trim(package->client, package->req);
                        break;
+               case NBD_CMD_WRITE_ZEROES:
+                       handle_write_zeroes(package->client, package->req);
+                       break;
                default:
                        msg(LOG_ERR, "E: received unknown command %d of type, 
ignoring", package->req->type);
                        goto error;
@@ -1646,7 +1709,7 @@ int mainloop(CLIENT *client) {
                memcpy(reply.handle, request.handle, sizeof(reply.handle));
 
                if ((command==NBD_CMD_WRITE) || (command==NBD_CMD_READ) ||
-                   (command==NBD_CMD_TRIM)) {
+                   (command==NBD_CMD_TRIM) || (command==NBD_CMD_WRITE_ZEROES)) 
{
                        if (request.from + len < request.from) { // 64 bit 
overflow!!
                                DEBUG("[Number too large!]");
                                ERROR(client, reply, EINVAL);
@@ -1655,7 +1718,7 @@ int mainloop(CLIENT *client) {
 
                        if (((off_t)request.from + len) > client->exportsize) {
                                DEBUG("[RANGE!]");
-                               ERROR(client, reply, (command==NBD_CMD_WRITE) ? 
ENOSPC : EINVAL);
+                               ERROR(client, reply, (command==NBD_CMD_WRITE || 
command==NBD_CMD_WRITE_ZEROES) ? ENOSPC : EINVAL);
                                continue;
                        }
 
@@ -1762,6 +1825,21 @@ int mainloop(CLIENT *client) {
                        SEND(client->net, reply);
                        continue;
 
+               case NBD_CMD_WRITE_ZEROES:
+                       if ((client->server->flags & F_READONLY) ||
+                           (client->server->flags & F_AUTOREADONLY)) {
+                               DEBUG("[WRITE_ZEROES to READONLY!]");
+                               ERROR(client, reply, EPERM);
+                               continue;
+                       }
+                       if (expwrite_zeroes(&request, client)) {
+                               DEBUG("Write zeroes failed: %m");
+                               ERROR(client, reply, errno);
+                               continue;
+                       }
+                       SEND(client->net, reply);
+                       continue;
+
                default:
                        DEBUG ("Ignoring unknown command\n");
                        continue;
diff --git a/nbd.h b/nbd.h
index 732c605..a5a1946 100644
--- a/nbd.h
+++ b/nbd.h
@@ -34,12 +34,14 @@ enum {
        NBD_CMD_WRITE = 1,
        NBD_CMD_DISC = 2,
        NBD_CMD_FLUSH = 3,
-       NBD_CMD_TRIM = 4
+       NBD_CMD_TRIM = 4,
+       NBD_CMD_WRITE_ZEROES = 6
 };
 
 #define NBD_CMD_MASK_COMMAND 0x0000ffff
 #define NBD_CMD_SHIFT (16)
 #define NBD_CMD_FLAG_FUA ((1 << 0) << NBD_CMD_SHIFT)
+#define NBD_CMD_FLAG_NO_HOLE ((1 << 1) << NBD_CMD_SHIFT)
 
 /* values for flags field */
 #define NBD_FLAG_HAS_FLAGS     (1 << 0)        /* Flags are there */
@@ -48,6 +50,7 @@ enum {
 #define NBD_FLAG_SEND_FUA      (1 << 3)        /* Send FUA (Force Unit Access) 
*/
 #define NBD_FLAG_ROTATIONAL    (1 << 4)        /* Use elevator algorithm - 
rotational media */
 #define NBD_FLAG_SEND_TRIM     (1 << 5)        /* Send TRIM (discard) */
+#define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6)    /* Send NBD_CMD_WRITE_ZEROES */
 
 #define nbd_cmd(req) ((req)->cmd[0])
 
-- 
1.9.1


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z
_______________________________________________
Nbd-general mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/nbd-general

Reply via email to