When structured replies are negotiated, the server may advertise support for the DF flag (the server promises to return at most one data/hole chunk, or to fail with EOVERFLOW if the chunk would be too large). As both nbdkit and qemu-nbd support this flag (the former only trivially, but the latter by not compressing holes over the wire), it is worth exposing to clients. While most clients will probably not be using it, especially as long as we don't have an aio method for getting at individual chunks, supporting the flag now at least helps in testing server implementations. --- generator/generator | 21 +++++++++++++++++++-- lib/flags.c | 11 +++++++++++ lib/rw.c | 8 +++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/generator/generator b/generator/generator index 919341c..3f300fd 100755 --- a/generator/generator +++ b/generator/generator @@ -1165,6 +1165,18 @@ the server does not. Can return an error if we have not connected to and completed the handshake with the server."; }; + "can_df", { + default_call with + args = []; ret = RBool; + shortdesc = "does the server support the don't fragment flag to pread?"; + longdesc = "\ +Returns true if the server supports structured reads with an +ability to request a non-fragmented read (see C<nbd_pread>, +C<nbd_aio_pread>). Returns false if the server does not. +Can return an error if we have not connected to and completed +the handshake with the server."; + }; + "can_multi_conn", { default_call with args = []; ret = RBool; @@ -1230,8 +1242,12 @@ can only read all or nothing using this call. The call returns when the data has been read fully into C<buf> or there is an error. -The C<flags> parameter must be C<0> for now (it exists for future NBD -protocol extensions)."; +The C<flags> parameter may be C<0> for no flags, or may contain +C<LIBNBD_CMD_FLAG_DF> meaning that the server should not fragment +a read reply across more than one packet, even if using multiple +packets would otherwise allow a more efficient representation of +holes contained in the region (if that is supported - some servers +cannot do this, see C<nbd_can_df>)."; }; "pwrite", { @@ -1774,6 +1790,7 @@ let constants = [ "CMD_FLAG_FUA", 1 lsl 0; "CMD_FLAG_NO_HOLE", 1 lsl 1; + "CMD_FLAG_DF", 1 lsl 2; "CMD_FLAG_REQ_ONE", 1 lsl 3; ] diff --git a/lib/flags.c b/lib/flags.c index 421a7d2..cdbc28f 100644 --- a/lib/flags.c +++ b/lib/flags.c @@ -42,6 +42,11 @@ nbd_internal_set_size_and_flags (struct nbd_handle *h, return -1; } + if (eflags & NBD_FLAG_SEND_DF && !h->structured_replies) { + debug (h, "server lacks structured replies, ignoring claim of df"); + eflags &= ~NBD_FLAG_SEND_DF; + } + h->exportsize = exportsize; h->eflags = eflags; return 0; @@ -95,6 +100,12 @@ nbd_unlocked_can_zero (struct nbd_handle *h) return get_flag (h, NBD_FLAG_SEND_WRITE_ZEROES); } +int +nbd_unlocked_can_df (struct nbd_handle *h) +{ + return get_flag (h, NBD_FLAG_SEND_DF); +} + int nbd_unlocked_can_multi_conn (struct nbd_handle *h) { diff --git a/lib/rw.c b/lib/rw.c index feaf468..343c340 100644 --- a/lib/rw.c +++ b/lib/rw.c @@ -234,11 +234,17 @@ int64_t nbd_unlocked_aio_pread (struct nbd_handle *h, void *buf, size_t count, uint64_t offset, uint32_t flags) { - if (flags != 0) { + if ((flags & ~LIBNBD_CMD_FLAG_DF) != 0) { set_error (EINVAL, "invalid flag: %" PRIu32, flags); return -1; } + if ((flags & LIBNBD_CMD_FLAG_DF) != 0 && + nbd_unlocked_can_df (h) != 1) { + set_error (EINVAL, "server does not support the DF flag"); + return -1; + } + return nbd_internal_command_common (h, 0, NBD_CMD_READ, offset, count, buf, NULL); } -- 2.20.1