Implement the logic to actually use FUA callbacks defined by a
plugin. This includes separating the tracking of advertising
flush vs. fua to clients, as it is feasible that a plugin might
support fua but not flush.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/connections.c | 15 ++++++++++++---
src/internal.h | 1 +
src/plugins.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++--------
3 files changed, 59 insertions(+), 11 deletions(-)
diff --git a/src/connections.c b/src/connections.c
index e4a0a82..2884f0e 100644
--- a/src/connections.c
+++ b/src/connections.c
@@ -79,6 +79,7 @@ struct connection {
int is_rotational;
int can_trim;
int using_tls;
+ int can_fua;
int sockin, sockout;
connection_recv_function recv;
@@ -367,10 +368,18 @@ compute_eflags (struct connection *conn, uint16_t *flags)
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;
}
+ fl = plugin_can_fua (conn);
+ if (fl == -1)
+ return -1;
+ if (fl) {
+ eflags |= NBD_FLAG_SEND_FUA;
+ conn->can_fua = 1;
+ }
+
fl = plugin_is_rotational (conn);
if (fl == -1)
return -1;
@@ -814,7 +823,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;
@@ -873,7 +882,7 @@ handle_request (struct connection *conn,
uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count,
void *buf)
{
- bool fua = conn->can_flush && (flags & NBD_CMD_FLAG_FUA);
+ bool fua = conn->can_fua && (flags & NBD_CMD_FLAG_FUA);
/* The plugin should call nbdkit_set_error() to request a particular
error, otherwise we fallback to errno or EIO. */
diff --git a/src/internal.h b/src/internal.h
index 3ab08d3..88a2eb8 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -163,6 +163,7 @@ extern int plugin_can_write (struct connection *conn);
extern int plugin_can_flush (struct connection *conn);
extern int plugin_is_rotational (struct connection *conn);
extern int plugin_can_trim (struct connection *conn);
+extern int plugin_can_fua (struct connection *conn);
extern int plugin_pread (struct connection *conn, void *buf, uint32_t count, uint64_t
offset);
extern int plugin_pwrite (struct connection *conn, void *buf, uint32_t count, uint64_t
offset, int fua);
extern int plugin_flush (struct connection *conn);
diff --git a/src/plugins.c b/src/plugins.c
index 61f0cc8..93cc535 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -271,6 +271,10 @@ plugin_dump_fields (void)
HAS (flush);
HAS (trim);
HAS (zero);
+ HAS (can_fua);
+ HAS (pwrite_fua);
+ HAS (zero_fua);
+ HAS (trim_fua);
#undef HAS
/* Custom fields. */
@@ -437,7 +441,7 @@ plugin_can_write (struct connection *conn)
if (plugin.can_write)
return plugin.can_write (connection_get_handle (conn));
else
- return plugin.pwrite != NULL;
+ return plugin.pwrite || plugin.pwrite_fua;
}
int
@@ -454,6 +458,21 @@ plugin_can_flush (struct connection *conn)
return plugin.flush != NULL;
}
+int
+plugin_can_fua (struct connection *conn)
+{
+ assert (dl);
+ assert (connection_get_handle (conn));
+
+ debug ("can_fua");
+
+ if (plugin.can_fua)
+ return plugin.can_fua (connection_get_handle (conn));
+ if (plugin.pwrite_fua)
+ return 1;
+ return plugin_can_flush (conn);
+}
+
int
plugin_is_rotational (struct connection *conn)
{
@@ -506,7 +525,12 @@ plugin_pwrite (struct connection *conn,
debug ("pwrite count=%" PRIu32 " offset=%" PRIu64 "
fua=%d", count, offset,
fua);
- if (plugin.pwrite != NULL)
+ if (plugin.pwrite_fua) {
+ r = plugin.pwrite_fua (connection_get_handle (conn), buf, count, offset,
+ fua);
+ fua = 0;
+ }
+ else if (plugin.pwrite != NULL)
r = plugin.pwrite (connection_get_handle (conn), buf, count, offset);
else {
errno = EROFS;
@@ -545,7 +569,11 @@ plugin_trim (struct connection *conn, uint32_t count, uint64_t
offset, int fua)
debug ("trim count=%" PRIu32 " offset=%" PRIu64 "
fua=%d", count, offset,
fua);
- if (plugin.trim != NULL)
+ if (plugin.trim_fua) {
+ r = plugin.trim_fua (connection_get_handle (conn), count, offset, fua);
+ fua = 0;
+ }
+ else if (plugin.trim)
r = plugin.trim (connection_get_handle (conn), count, offset);
else {
errno = EINVAL;
@@ -574,19 +602,25 @@ plugin_zero (struct connection *conn,
if (!count)
return 0;
- if (plugin.zero) {
+ if (plugin.zero || plugin.zero_fua) {
errno = 0;
- result = plugin.zero (connection_get_handle (conn), count, offset, may_trim);
+ result = plugin.zero_fua
+ ? plugin.zero_fua (connection_get_handle (conn), count, offset, may_trim,
+ fua)
+ : plugin.zero (connection_get_handle (conn), count, offset, may_trim);
if (result == -1) {
err = threadlocal_get_error ();
if (!err && plugin_errno_is_preserved ())
err = errno;
}
- if (result == 0 || err != EOPNOTSUPP)
+ if (result == 0 || err != EOPNOTSUPP) {
+ if (plugin.zero_fua)
+ fua = 0;
goto done;
+ }
}
- assert (plugin.pwrite);
+ assert (plugin.pwrite || plugin.pwrite_fua);
threadlocal_set_error (0);
limit = count < MAX_REQUEST_SIZE ? count : MAX_REQUEST_SIZE;
buf = calloc (limit, 1);
@@ -595,14 +629,18 @@ plugin_zero (struct connection *conn,
return -1;
}
+ /* If we have to emulate FUA, we can flush once at the end rather
+ than on each write iteration */
while (count) {
- result = plugin.pwrite (connection_get_handle (conn), buf, limit, offset);
+ result = plugin_pwrite (conn, buf, limit, offset, fua && plugin.pwrite_fua);
if (result < 0)
break;
count -= limit;
if (count < limit)
limit = count;
}
+ if (plugin.pwrite_fua)
+ fua = 0;
err = errno;
free (buf);
--
2.14.3