While our plugin code always advertises WRITE_ZEROES on writable
connections (because we can emulate .zero by calling .pwrite),
and FUA support when .flush exists (because we can emulate the
persistence by calling .flush), it is conceivable that a filter
may want to explicitly avoid advertising particular bits. More
to the point, upcoming patches will add a 'nozero' filter that
hides WRITE_ZEROES support, at least for the purposes of timing
comparisons between server emulation and the client having to
send zeroes over the wire; as well as support for letting plugins
and filters directly manage the level of FUA support.
Since we only have to honor FUA support on write requests, we do
not need to advertise it on a readonly connection.
Wire up two new backend callbacks. .can_zero has the traditional
boolean+error semantics, but .can_fua has three return values
besides errors - this is because down the road, the decision on
whether to pass FUA to the next layer when subdividing a larger
request into smaller ones is dependent on knowing whether FUA is
emulated by .flush (flush only once after the last chunk) or
handled natively (pass FUA on every chunk).
Later patches will then expose both backend helpers to filters,
and .can_fua to plugins.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/internal.h | 8 ++++++++
src/connections.c | 31 +++++++++++++++++++++++++++----
src/filters.c | 24 ++++++++++++++++++++++++
src/plugins.c | 30 ++++++++++++++++++++++++++++++
4 files changed, 89 insertions(+), 4 deletions(-)
diff --git a/src/internal.h b/src/internal.h
index 12dfc14..b49f071 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -98,6 +98,10 @@
(type *) ((char *) __mptr - offsetof(type, member)); \
})
+#define NBDKIT_FUA_NONE 0
+#define NBDKIT_FUA_EMULATE 1
+#define NBDKIT_FUA_NATIVE 2
+
/* main.c */
extern const char *exportname;
extern const char *ipaddr;
@@ -177,11 +181,15 @@ struct backend {
int (*prepare) (struct backend *, struct connection *conn);
int (*finalize) (struct backend *, struct connection *conn);
void (*close) (struct backend *, struct connection *conn);
+
int64_t (*get_size) (struct backend *, struct connection *conn);
int (*can_write) (struct backend *, struct connection *conn);
int (*can_flush) (struct backend *, struct connection *conn);
int (*is_rotational) (struct backend *, struct connection *conn);
int (*can_trim) (struct backend *, struct connection *conn);
+ int (*can_zero) (struct backend *, struct connection *conn);
+ int (*can_fua) (struct backend *, struct connection *conn);
+
int (*pread) (struct backend *, struct connection *conn, void *buf,
uint32_t count, uint64_t offset, uint32_t flags, int *err);
int (*pwrite) (struct backend *, struct connection *conn, const void *buf,
diff --git a/src/connections.c b/src/connections.c
index 03f59f7..fbcf5e5 100644
--- a/src/connections.c
+++ b/src/connections.c
@@ -80,6 +80,8 @@ struct connection {
int can_flush;
int is_rotational;
int can_trim;
+ int can_zero;
+ int can_fua;
int using_tls;
int sockin, sockout;
@@ -426,7 +428,13 @@ compute_eflags (struct connection *conn, uint16_t *flags)
conn->readonly = 1;
}
if (!conn->readonly) {
- eflags |= NBD_FLAG_SEND_WRITE_ZEROES;
+ fl = backend->can_zero (backend, conn);
+ if (fl == -1)
+ return -1;
+ if (fl) {
+ eflags |= NBD_FLAG_SEND_WRITE_ZEROES;
+ conn->can_zero = 1;
+ }
fl = backend->can_trim (backend, conn);
if (fl == -1)
@@ -435,13 +443,21 @@ compute_eflags (struct connection *conn, uint16_t *flags)
eflags |= NBD_FLAG_SEND_TRIM;
conn->can_trim = 1;
}
+
+ fl = backend->can_fua (backend, conn);
+ if (fl == -1)
+ return -1;
+ if (fl) {
+ eflags |= NBD_FLAG_SEND_FUA;
+ conn->can_fua = 1;
+ }
}
fl = backend->can_flush (backend, conn);
if (fl == -1)
return -1;
if (fl) {
- eflags |= NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA;
+ eflags |= NBD_FLAG_SEND_FLUSH;
conn->can_flush = 1;
}
@@ -880,7 +896,7 @@ validate_request (struct connection *conn,
*error = EINVAL;
return false;
}
- if (!conn->can_flush && (flags & NBD_CMD_FLAG_FUA)) {
+ if (!conn->can_fua && (flags & NBD_CMD_FLAG_FUA)) {
nbdkit_error ("invalid request: FUA flag not supported");
*error = EINVAL;
return false;
@@ -909,6 +925,13 @@ validate_request (struct connection *conn,
return false;
}
+ /* Zero allowed? */
+ if (!conn->can_zero && cmd == NBD_CMD_WRITE_ZEROES) {
+ nbdkit_error ("invalid request: write zeroes operation not supported");
+ *error = EINVAL;
+ return false;
+ }
+
return true; /* Command validates. */
}
@@ -928,7 +951,7 @@ handle_request (struct connection *conn,
void *buf)
{
uint32_t f = 0;
- bool fua = conn->can_flush && (flags & NBD_CMD_FLAG_FUA);
+ bool fua = conn->can_fua && (flags & NBD_CMD_FLAG_FUA);
int err = 0;
/* Clear the error, so that we know if the plugin calls
diff --git a/src/filters.c b/src/filters.c
index 9d821f2..0cf7594 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -446,6 +446,28 @@ filter_can_trim (struct backend *b, struct connection *conn)
return f->backend.next->can_trim (f->backend.next, conn);
}
+static int
+filter_can_zero (struct backend *b, struct connection *conn)
+{
+ struct backend_filter *f = container_of (b, struct backend_filter, backend);
+
+ debug ("can_zero");
+
+ /* TODO expose this to filters */
+ return f->backend.next->can_zero (f->backend.next, conn);
+}
+
+static int
+filter_can_fua (struct backend *b, struct connection *conn)
+{
+ struct backend_filter *f = container_of (b, struct backend_filter, backend);
+
+ debug ("can_fua");
+
+ /* TODO expose this to plugins and filters */
+ return f->backend.next->can_fua (f->backend.next, conn);
+}
+
static int
filter_pread (struct backend *b, struct connection *conn,
void *buf, uint32_t count, uint64_t offset,
@@ -570,6 +592,8 @@ static struct backend filter_functions = {
.can_flush = filter_can_flush,
.is_rotational = filter_is_rotational,
.can_trim = filter_can_trim,
+ .can_zero = filter_can_zero,
+ .can_fua = filter_can_fua,
.pread = filter_pread,
.pwrite = filter_pwrite,
.flush = filter_flush,
diff --git a/src/plugins.c b/src/plugins.c
index d5a2a65..10c911d 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -361,6 +361,34 @@ get_error (struct backend_plugin *p)
return ret ? ret : EIO;
}
+static int
+plugin_can_zero (struct backend *b, struct connection *conn)
+{
+ debug ("can_zero");
+
+ /* We always allow .zero to fall back to .write, so plugins don't
+ * need to override this. */
+ return plugin_can_write (b, conn);
+}
+
+static int
+plugin_can_fua (struct backend *b, struct connection *conn)
+{
+ struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+ int r;
+
+ debug ("can_fua");
+
+ /* TODO - wire FUA flag support into plugins. Until then, this copies
+ * can_flush, since that's how we emulate FUA. */
+ r = plugin_can_flush (b, conn);
+ if (r == -1)
+ return -1;
+ if (r == 0 || !p->plugin.flush)
+ return NBDKIT_FUA_NONE;
+ return NBDKIT_FUA_EMULATE;
+}
+
static int
plugin_pread (struct backend *b, struct connection *conn,
void *buf, uint32_t count, uint64_t offset, uint32_t flags,
@@ -546,6 +574,8 @@ static struct backend plugin_functions = {
.can_flush = plugin_can_flush,
.is_rotational = plugin_is_rotational,
.can_trim = plugin_can_trim,
+ .can_zero = plugin_can_zero,
+ .can_fua = plugin_can_fua,
.pread = plugin_pread,
.pwrite = plugin_pwrite,
.flush = plugin_flush,
--
2.14.3