medium, and the client MAY schedule I/O accesses in a manner corresponding
to the setting of this flag.
- bit 5, `NBD_FLAG_SEND_TRIM`: exposes support for `NBD_CMD_TRIM`.
-- bit 6, `NBD_FLAG_SEND_WRITE_ZEROES`: defined by the
- experimental `WRITE_ZEROES` [extension](https://github.com/NetworkBlockDevice/nbd/blob/extension-write-zeroes/doc/proto.md).
+- bit 6, `NBD_FLAG_SEND_WRITE_ZEROES`: exposes support for
+ `NBD_CMD_WRITE_ZEROES` and `NBD_CMD_FLAG_NO_HOLE`.
- bit 7, `NBD_FLAG_SEND_DF`: defined by the experimental `STRUCTURED_REPLY`
- [extension](https://github.com/yoe/nbd/blob/extension-structured-reply/doc/proto.md).
+ [extension](https://github.com/NetworkBlockDevice/nbd/blob/extension-structured-reply/doc/proto.md).
+ - bit 8, `NBD_FLAG_CAN_MULTI_CONN`: Indicates that the server operates
+ entirely without cache, or that the cache it uses is shared among all
+ connections to the given device. In particular, if this flag is
+ present, then the effects of `NBD_CMD_FLUSH` and `NBD_CMD_FLAG_FUA`
+ MUST be visible across all connections when the server sends its reply
+ to that command to the client. In the absense of this flag, clients
+ SHOULD NOT multiplex their commands over more than one connection to
+ the export.
+ - bit 9, `NBD_FLAG_SEND_BLOCK_STATUS`: defined by the experimental
+ `BLOCK_STATUS` [extension](https://github.com/NetworkBlockDevice/nbd/blob/extension-blockstatus/doc/proto.md).
Clients SHOULD ignore unknown flags.
not in fact write data (for instance on an `NBD_CMD_TRIM` in a situation
where the command as a whole is ignored), the server MAY ignore this bit
being set on such a command.
-- bit 1, `NBD_CMD_FLAG_NO_HOLE`; defined by the experimental `WRITE_ZEROES`
- [extension](https://github.com/NetworkBlockDevice/nbd/blob/extension-write-zeroes/doc/proto.md).
+- bit 1, `NBD_CMD_FLAG_NO_HOLE`; valid during `NBD_CMD_WRITE_ZEROES`.
+ SHOULD be set to 1 if the client wants to ensure that the server does
+ not create a hole. The client MAY send `NBD_CMD_FLAG_NO_HOLE` even
+ if `NBD_FLAG_SEND_TRIM` was not set in the transmission flags field.
+ The server MUST support the use of this flag if it advertises
+ `NBD_FLAG_SEND_WRITE_ZEROES`.
- bit 2, `NBD_CMD_FLAG_DF`; defined by the experimental `STRUCTURED_REPLY`
- [extension](https://github.com/yoe/nbd/blob/extension-structured-reply/doc/proto.md).
+ [extension](https://github.com/NetworkBlockDevice/nbd/blob/extension-structured-reply/doc/proto.md).
#### Request types
return 0;
}
- int expwrite_zeroes(struct nbd_request* req, CLIENT* client) {
+
+/**
+ * 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 fua = !!(req->type & NBD_CMD_FLAG_FUA);
++int expwrite_zeroes(struct nbd_request* req, CLIENT* client, int fua) {
+ off_t a = req->from;
+ size_t len = req->len;
+ 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
*
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");
+ socket_write(client, &size_host, 8);
if (client->server->flags & F_READONLY)
flags |= NBD_FLAG_READ_ONLY;
if (client->server->flags & F_FLUSH)
free(buf);
}
- static void handle_write(CLIENT* client, struct nbd_request* req, void* data) {
+ static void handle_read(CLIENT* client, struct nbd_request* req)
+ {
+ #ifdef HAVE_SPLICE
+ /*
+ * If we have splice set we want to try that first, and if that fails
+ * for whatever reason we fall through to ye olde read.
+ */
+ if (client->server->flags & F_SPLICE)
+ if (!handle_splice_read(client, req))
+ return;
+ #endif
+ handle_normal_read(client, req);
+ }
+
+ static void handle_write(struct work_package *pkg)
+ {
+ CLIENT *client = pkg->client;
+ struct nbd_request *req = pkg->req;
struct nbd_reply rep;
- int fua = req->type & ~NBD_CMD_MASK_COMMAND;
++ int fua = !!(req->type & NBD_CMD_FLAG_FUA);
+
DEBUG("handling write request\n");
setup_reply(&rep, req);
pthread_mutex_unlock(&(client->lock));
}
- if(expwrite_zeroes(req, client)) {
+static void handle_write_zeroes(CLIENT* client, struct nbd_request* req) {
+ struct nbd_reply rep;
+ DEBUG("handling write_zeroes request\n");
++ int fua = !!(req->type & NBD_CMD_FLAG_FUA);
+ setup_reply(&rep, req);
++ if ((client->server->flags & F_READONLY) ||
++ (client->server->flags & F_AUTOREADONLY)) {
++ DEBUG("[WRITE to READONLY!]");
++ rep.error = nbd_errno(EPERM);
++ } else if(expwrite_zeroes(req, client, fua)) {
+ DEBUG("Write_zeroes failed: %m");
+ rep.error = nbd_errno(errno);
+ }
+ // For now, don't trim
+ // TODO: handle this far more efficiently with reference to the
+ // actual backing driver
+ 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;
ERROR(client, reply, errno);
continue;
}
- SEND(client->net, reply);
+ SEND(client, reply);
continue;
- if (expwrite_zeroes(&request, client)) {
+ 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;
+ }
- SEND(client->net, reply);
++ if (expwrite_zeroes(&request, client,
++ request.type & NBD_CMD_FLAG_FUA)) {
+ DEBUG("Write zeroes failed: %m");
+ ERROR(client, reply, errno);
+ continue;
+ }
++ SEND(client, reply);
+ continue;
+
default:
DEBUG ("Ignoring unknown command\n");
continue;