Skip to content

Commit

Permalink
nbd: Implement NBD_CMD_WRITE_ZEROES on server
Browse files Browse the repository at this point in the history
Upstream NBD protocol recently added the ability to efficiently
write zeroes without having to send the zeroes over the wire,
along with a flag to control whether the client wants to allow
a hole.

Note that when it comes to requiring full allocation, vs.
permitting optimizations, the NBD spec intentionally picked a
different sense for the flag; the rules in qemu are:
MAY_UNMAP == 0: must write zeroes
MAY_UNMAP == 1: may use holes if reads will see zeroes

while in NBD, the rules are:
FLAG_NO_HOLE == 1: must write zeroes
FLAG_NO_HOLE == 0: may use holes if reads will see zeroes

In all cases, the 'may use holes' scenario is optional (the
server need not use a hole, and must not use a hole if
subsequent reads would not see zeroes).

Signed-off-by: Eric Blake <[email protected]>
Message-Id: <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
  • Loading branch information
ebblake authored and bonzini committed Nov 2, 2016
1 parent b6f5d3b commit 1f4d6d1
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 4 deletions.
8 changes: 6 additions & 2 deletions include/block/nbd.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ typedef struct NBDReply NBDReply;
#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 WRITE_ZEROES */

/* New-style handshake (global) flags, sent from server to client, and
control what will happen during handshake phase. */
Expand All @@ -96,15 +97,18 @@ typedef struct NBDReply NBDReply;
#define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */

/* Request flags, sent from client to server during transmission phase */
#define NBD_CMD_FLAG_FUA (1 << 0)
#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */
#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */

/* Supported request types */
enum {
NBD_CMD_READ = 0,
NBD_CMD_WRITE = 1,
NBD_CMD_DISC = 2,
NBD_CMD_FLUSH = 3,
NBD_CMD_TRIM = 4
NBD_CMD_TRIM = 4,
/* 5 reserved for failed experiment NBD_CMD_CACHE */
NBD_CMD_WRITE_ZEROES = 6,
};

#define NBD_DEFAULT_PORT 10809
Expand Down
42 changes: 40 additions & 2 deletions nbd/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,8 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
char buf[8 + 8 + 8 + 128];
int rc;
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA);
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
NBD_FLAG_SEND_WRITE_ZEROES);
bool oldStyle;
size_t len;

Expand Down Expand Up @@ -1146,11 +1147,17 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
rc = request->type == NBD_CMD_WRITE ? -ENOSPC : -EINVAL;
goto out;
}
if (request->flags & ~NBD_CMD_FLAG_FUA) {
if (request->flags & ~(NBD_CMD_FLAG_FUA | NBD_CMD_FLAG_NO_HOLE)) {
LOG("unsupported flags (got 0x%x)", request->flags);
rc = -EINVAL;
goto out;
}
if (request->type != NBD_CMD_WRITE_ZEROES &&
(request->flags & NBD_CMD_FLAG_NO_HOLE)) {
LOG("unexpected flags (got 0x%x)", request->flags);
rc = -EINVAL;
goto out;
}

rc = 0;

Expand Down Expand Up @@ -1255,6 +1262,37 @@ static void nbd_trip(void *opaque)
}
break;

case NBD_CMD_WRITE_ZEROES:
TRACE("Request type is WRITE_ZEROES");

if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
TRACE("Server is read-only, return error");
reply.error = EROFS;
goto error_reply;
}

TRACE("Writing to device");

flags = 0;
if (request.flags & NBD_CMD_FLAG_FUA) {
flags |= BDRV_REQ_FUA;
}
if (!(request.flags & NBD_CMD_FLAG_NO_HOLE)) {
flags |= BDRV_REQ_MAY_UNMAP;
}
ret = blk_pwrite_zeroes(exp->blk, request.from + exp->dev_offset,
request.len, flags);
if (ret < 0) {
LOG("writing to file failed");
reply.error = -ret;
goto error_reply;
}

if (nbd_co_send_reply(req, &reply, 0) < 0) {
goto out;
}
break;

case NBD_CMD_DISC:
/* unreachable, thanks to special case in nbd_co_receive_request() */
abort();
Expand Down

0 comments on commit 1f4d6d1

Please sign in to comment.