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 <[email protected]> --- 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 _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
