As long as the plugins couldn't directly handle Forced Unit Access (FUA), it made sense to have a common fallback in handle_request(). But upcoming patches will allow plugins to handle FUA, at which point the fallback is easier to implement on a per-command basis, via an extra parameter to the plugins wrapper code.
We also add an additional validation to commands sent by the client: if we did not advertise FUA flag support (because the plugin does not support flush), then the client should not be requesting FUA; this in turn means that we can assert that the flush callback is valid if the plugins layer gets a fua flag. Signed-off-by: Eric Blake <[email protected]> --- src/connections.c | 21 +++++++++------------ src/internal.h | 8 ++++---- src/plugins.c | 42 +++++++++++++++++++++++++++++++----------- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/connections.c b/src/connections.c index 24df4b6..e4a0a82 100644 --- a/src/connections.c +++ b/src/connections.c @@ -814,6 +814,11 @@ validate_request (struct connection *conn, *error = EINVAL; return false; } + if (!conn->can_flush && (flags & NBD_CMD_FLAG_FUA)) { + nbdkit_error ("invalid request: FUA flag not supported"); + *error = EINVAL; + return false; + } /* Refuse over-large read and write requests. */ if ((cmd == NBD_CMD_WRITE || cmd == NBD_CMD_READ) && @@ -868,10 +873,7 @@ handle_request (struct connection *conn, uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count, void *buf) { - bool flush_after_command; - - /* Flush after command performed? */ - flush_after_command = conn->can_flush && (flags & NBD_CMD_FLAG_FUA); + bool fua = conn->can_flush && (flags & NBD_CMD_FLAG_FUA); /* The plugin should call nbdkit_set_error() to request a particular error, otherwise we fallback to errno or EIO. */ @@ -879,30 +881,28 @@ handle_request (struct connection *conn, switch (cmd) { case NBD_CMD_READ: - flush_after_command = false; if (plugin_pread (conn, buf, count, offset) == -1) return get_error (conn); break; case NBD_CMD_WRITE: - if (plugin_pwrite (conn, buf, count, offset) == -1) + if (plugin_pwrite (conn, buf, count, offset, fua) == -1) return get_error (conn); break; case NBD_CMD_FLUSH: - flush_after_command = false; if (plugin_flush (conn) == -1) return get_error (conn); break; case NBD_CMD_TRIM: - if (plugin_trim (conn, count, offset) == -1) + if (plugin_trim (conn, count, offset, fua) == -1) return get_error (conn); break; case NBD_CMD_WRITE_ZEROES: if (plugin_zero (conn, count, offset, - !(flags & NBD_CMD_FLAG_NO_HOLE)) == -1) + !(flags & NBD_CMD_FLAG_NO_HOLE), fua) == -1) return get_error (conn); break; @@ -910,9 +910,6 @@ handle_request (struct connection *conn, abort (); } - if (flush_after_command && plugin_flush (conn) == -1) - return get_error (conn); - return 0; } diff --git a/src/internal.h b/src/internal.h index 73bc09e..3ab08d3 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1,5 +1,5 @@ /* nbdkit - * Copyright (C) 2013-2017 Red Hat Inc. + * Copyright (C) 2013-2018 Red Hat Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -164,10 +164,10 @@ 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_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); +extern int plugin_pwrite (struct connection *conn, void *buf, uint32_t count, uint64_t offset, int fua); extern int plugin_flush (struct connection *conn); -extern int plugin_trim (struct connection *conn, uint32_t count, uint64_t offset); -extern int plugin_zero (struct connection *conn, uint32_t count, uint64_t offset, int may_trim); +extern int plugin_trim (struct connection *conn, uint32_t count, uint64_t offset, int fua); +extern int plugin_zero (struct connection *conn, uint32_t count, uint64_t offset, int may_trim, int fua); /* sockets.c */ extern int *bind_unix_socket (size_t *); diff --git a/src/plugins.c b/src/plugins.c index 9b5d2d5..61f0cc8 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -1,5 +1,5 @@ /* nbdkit - * Copyright (C) 2013 Red Hat Inc. + * Copyright (C) 2013-2018 Red Hat Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -497,19 +497,26 @@ plugin_pread (struct connection *conn, int plugin_pwrite (struct connection *conn, - void *buf, uint32_t count, uint64_t offset) + void *buf, uint32_t count, uint64_t offset, int fua) { + int r; assert (dl); assert (connection_get_handle (conn)); - debug ("pwrite count=%" PRIu32 " offset=%" PRIu64, count, offset); + debug ("pwrite count=%" PRIu32 " offset=%" PRIu64 " fua=%d", count, offset, + fua); if (plugin.pwrite != NULL) - return plugin.pwrite (connection_get_handle (conn), buf, count, offset); + r = plugin.pwrite (connection_get_handle (conn), buf, count, offset); else { errno = EROFS; return -1; } + if (r == 0 && fua) { + assert (plugin.flush); + r = plugin_flush (conn); + } + return r; } int @@ -529,24 +536,31 @@ plugin_flush (struct connection *conn) } int -plugin_trim (struct connection *conn, uint32_t count, uint64_t offset) +plugin_trim (struct connection *conn, uint32_t count, uint64_t offset, int fua) { + int r; assert (dl); assert (connection_get_handle (conn)); - debug ("trim count=%" PRIu32 " offset=%" PRIu64, count, offset); + debug ("trim count=%" PRIu32 " offset=%" PRIu64 " fua=%d", count, offset, + fua); if (plugin.trim != NULL) - return plugin.trim (connection_get_handle (conn), count, offset); + r = plugin.trim (connection_get_handle (conn), count, offset); else { errno = EINVAL; return -1; } + if (r == 0 && fua) { + assert (plugin.flush); + r = plugin_flush (conn); + } + return r; } int plugin_zero (struct connection *conn, - uint32_t count, uint64_t offset, int may_trim) + uint32_t count, uint64_t offset, int may_trim, int fua) { assert (dl); assert (connection_get_handle (conn)); @@ -555,8 +569,8 @@ plugin_zero (struct connection *conn, int result; int err = 0; - debug ("zero count=%" PRIu32 " offset=%" PRIu64 " may_trim=%d", - count, offset, may_trim); + debug ("zero count=%" PRIu32 " offset=%" PRIu64 " may_trim=%d fua=%d", + count, offset, may_trim, fua); if (!count) return 0; @@ -569,7 +583,7 @@ plugin_zero (struct connection *conn, err = errno; } if (result == 0 || err != EOPNOTSUPP) - return result; + goto done; } assert (plugin.pwrite); @@ -593,5 +607,11 @@ plugin_zero (struct connection *conn, err = errno; free (buf); errno = err; + + done: + if (!result && fua) { + assert (plugin.flush); + result = plugin_flush (conn); + } return result; } -- 2.14.3 _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
