Skip to content

Commit

Permalink
nbd: Improve per-export flag handling in server
Browse files Browse the repository at this point in the history
When creating a read-only image, we are still advertising support for
TRIM and WRITE_ZEROES to the client, even though the client should not
be issuing those commands.  But seeing this requires looking across
multiple functions:

All callers to nbd_export_new() passed a single flag based solely on
whether the export allows writes.  Later, we then pass a constant set
of flags to nbd_negotiate_options() (namely, the set of flags which we
always support, at least for writable images), which is then further
dynamically modified with NBD_FLAG_SEND_DF based on client requests
for structured options.  Finally, when processing NBD_OPT_EXPORT_NAME
or NBD_OPT_EXPORT_GO we bitwise-or the original caller's flag with the
runtime set of flags we've built up over several functions.

Let's refactor things to instead compute a baseline of flags as soon
as possible which gets shared between multiple clients, in
nbd_export_new(), and changing the signature for the callers to pass
in a simpler bool rather than having to figure out flags.  We can then
get rid of the 'myflags' parameter to various functions, and instead
refer to client for everything we need (we still have to perform a
bitwise-OR for NBD_FLAG_SEND_DF during NBD_OPT_EXPORT_NAME and
NBD_OPT_EXPORT_GO, but it's easier to see what is being computed).
This lets us quit advertising senseless flags for read-only images, as
well as making the next patch for exposing FAST_ZERO support easier to
write.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20190823143726.27062-2-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
[eblake: improve commit message, update iotest 223]
  • Loading branch information
ebblake committed Sep 5, 2019
1 parent 0c61ebb commit dbb38ca
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 35 deletions.
3 changes: 1 addition & 2 deletions blockdev-nbd.c
Expand Up @@ -187,8 +187,7 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
writable = false;
}

exp = nbd_export_new(bs, 0, len, name, NULL, bitmap,
writable ? 0 : NBD_FLAG_READ_ONLY, !writable,
exp = nbd_export_new(bs, 0, len, name, NULL, bitmap, !writable, !writable,
NULL, false, on_eject_blk, errp);
if (!exp) {
return;
Expand Down
2 changes: 1 addition & 1 deletion include/block/nbd.h
Expand Up @@ -326,7 +326,7 @@ typedef struct NBDClient NBDClient;

NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
uint64_t size, const char *name, const char *desc,
const char *bitmap, uint16_t nbdflags, bool shared,
const char *bitmap, bool readonly, bool shared,
void (*close)(NBDExport *), bool writethrough,
BlockBackend *on_eject_blk, Error **errp);
void nbd_export_close(NBDExport *exp);
Expand Down
62 changes: 34 additions & 28 deletions nbd/server.c
Expand Up @@ -419,14 +419,14 @@ static void nbd_check_meta_export(NBDClient *client)

/* Send a reply to NBD_OPT_EXPORT_NAME.
* Return -errno on error, 0 on success. */
static int nbd_negotiate_handle_export_name(NBDClient *client,
uint16_t myflags, bool no_zeroes,
static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes,
Error **errp)
{
char name[NBD_MAX_NAME_SIZE + 1];
char buf[NBD_REPLY_EXPORT_NAME_SIZE] = "";
size_t len;
int ret;
uint16_t myflags;

/* Client sends:
[20 .. xx] export name (length bytes)
Expand Down Expand Up @@ -454,10 +454,13 @@ static int nbd_negotiate_handle_export_name(NBDClient *client,
return -EINVAL;
}

trace_nbd_negotiate_new_style_size_flags(client->exp->size,
client->exp->nbdflags | myflags);
myflags = client->exp->nbdflags;
if (client->structured_reply) {
myflags |= NBD_FLAG_SEND_DF;
}
trace_nbd_negotiate_new_style_size_flags(client->exp->size, myflags);
stq_be_p(buf, client->exp->size);
stw_be_p(buf + 8, client->exp->nbdflags | myflags);
stw_be_p(buf + 8, myflags);
len = no_zeroes ? 10 : sizeof(buf);
ret = nbd_write(client->ioc, buf, len, errp);
if (ret < 0) {
Expand Down Expand Up @@ -522,8 +525,7 @@ static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp)
/* Handle NBD_OPT_INFO and NBD_OPT_GO.
* Return -errno on error, 0 if ready for next option, and 1 to move
* into transmission phase. */
static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags,
Error **errp)
static int nbd_negotiate_handle_info(NBDClient *client, Error **errp)
{
int rc;
char name[NBD_MAX_NAME_SIZE + 1];
Expand All @@ -536,6 +538,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags,
uint32_t sizes[3];
char buf[sizeof(uint64_t) + sizeof(uint16_t)];
uint32_t check_align = 0;
uint16_t myflags;

/* Client sends:
4 bytes: L, name length (can be 0)
Expand Down Expand Up @@ -633,10 +636,13 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags,
}

/* Send NBD_INFO_EXPORT always */
trace_nbd_negotiate_new_style_size_flags(exp->size,
exp->nbdflags | myflags);
myflags = exp->nbdflags;
if (client->structured_reply) {
myflags |= NBD_FLAG_SEND_DF;
}
trace_nbd_negotiate_new_style_size_flags(exp->size, myflags);
stq_be_p(buf, exp->size);
stw_be_p(buf + 8, exp->nbdflags | myflags);
stw_be_p(buf + 8, myflags);
rc = nbd_negotiate_send_info(client, NBD_INFO_EXPORT,
sizeof(buf), buf, errp);
if (rc < 0) {
Expand Down Expand Up @@ -1033,8 +1039,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client,
* 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect,
* errp is not set
*/
static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
Error **errp)
static int nbd_negotiate_options(NBDClient *client, Error **errp)
{
uint32_t flags;
bool fixedNewstyle = false;
Expand Down Expand Up @@ -1168,13 +1173,12 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
return 1;

case NBD_OPT_EXPORT_NAME:
return nbd_negotiate_handle_export_name(client,
myflags, no_zeroes,
return nbd_negotiate_handle_export_name(client, no_zeroes,
errp);

case NBD_OPT_INFO:
case NBD_OPT_GO:
ret = nbd_negotiate_handle_info(client, myflags, errp);
ret = nbd_negotiate_handle_info(client, errp);
if (ret == 1) {
assert(option == NBD_OPT_GO);
return 0;
Expand Down Expand Up @@ -1205,7 +1209,6 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
} else {
ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
client->structured_reply = true;
myflags |= NBD_FLAG_SEND_DF;
}
break;

Expand All @@ -1228,8 +1231,7 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
*/
switch (option) {
case NBD_OPT_EXPORT_NAME:
return nbd_negotiate_handle_export_name(client,
myflags, no_zeroes,
return nbd_negotiate_handle_export_name(client, no_zeroes,
errp);

default:
Expand All @@ -1255,9 +1257,6 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
{
char buf[NBD_OLDSTYLE_NEGOTIATE_SIZE] = "";
int ret;
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
NBD_FLAG_SEND_WRITE_ZEROES | NBD_FLAG_SEND_CACHE);

/* Old style negotiation header, no room for options
[ 0 .. 7] passwd ("NBDMAGIC")
Expand Down Expand Up @@ -1285,7 +1284,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
error_prepend(errp, "write failed: ");
return -EINVAL;
}
ret = nbd_negotiate_options(client, myflags, errp);
ret = nbd_negotiate_options(client, errp);
if (ret != 0) {
if (ret < 0) {
error_prepend(errp, "option negotiation failed: ");
Expand Down Expand Up @@ -1457,7 +1456,7 @@ static void nbd_eject_notifier(Notifier *n, void *data)

NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
uint64_t size, const char *name, const char *desc,
const char *bitmap, uint16_t nbdflags, bool shared,
const char *bitmap, bool readonly, bool shared,
void (*close)(NBDExport *), bool writethrough,
BlockBackend *on_eject_blk, Error **errp)
{
Expand All @@ -1481,10 +1480,8 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
/* Don't allow resize while the NBD server is running, otherwise we don't
* care what happens with the node. */
perm = BLK_PERM_CONSISTENT_READ;
if ((nbdflags & NBD_FLAG_READ_ONLY) == 0) {
if (!readonly) {
perm |= BLK_PERM_WRITE;
} else if (shared) {
nbdflags |= NBD_FLAG_CAN_MULTI_CONN;
}
blk = blk_new(bdrv_get_aio_context(bs), perm,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
Expand All @@ -1503,7 +1500,16 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
exp->dev_offset = dev_offset;
exp->name = g_strdup(name);
exp->description = g_strdup(desc);
exp->nbdflags = nbdflags;
exp->nbdflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_FLUSH |
NBD_FLAG_SEND_FUA | NBD_FLAG_SEND_CACHE);
if (readonly) {
exp->nbdflags |= NBD_FLAG_READ_ONLY;
if (shared) {
exp->nbdflags |= NBD_FLAG_CAN_MULTI_CONN;
}
} else {
exp->nbdflags |= NBD_FLAG_SEND_TRIM | NBD_FLAG_SEND_WRITE_ZEROES;
}
assert(size <= INT64_MAX - dev_offset);
exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE);

Expand All @@ -1528,7 +1534,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
goto fail;
}

if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) &&
if (readonly && bdrv_is_writable(bs) &&
bdrv_dirty_bitmap_enabled(bm)) {
error_setg(errp,
"Enabled bitmap '%s' incompatible with readonly export",
Expand Down
6 changes: 3 additions & 3 deletions qemu-nbd.c
Expand Up @@ -600,7 +600,7 @@ int main(int argc, char **argv)
BlockBackend *blk;
BlockDriverState *bs;
uint64_t dev_offset = 0;
uint16_t nbdflags = 0;
bool readonly = false;
bool disconnect = false;
const char *bindto = NULL;
const char *port = NULL;
Expand Down Expand Up @@ -782,7 +782,7 @@ int main(int argc, char **argv)
}
/* fall through */
case 'r':
nbdflags |= NBD_FLAG_READ_ONLY;
readonly = true;
flags &= ~BDRV_O_RDWR;
break;
case 'P':
Expand Down Expand Up @@ -1173,7 +1173,7 @@ int main(int argc, char **argv)
}

export = nbd_export_new(bs, dev_offset, fd_size, export_name,
export_description, bitmap, nbdflags, shared > 1,
export_description, bitmap, readonly, shared > 1,
nbd_export_closed, writethrough, NULL,
&error_fatal);

Expand Down
2 changes: 1 addition & 1 deletion tests/qemu-iotests/223.out
Expand Up @@ -40,7 +40,7 @@ exports available: 0
exports available: 2
export: 'n'
size: 4194304
flags: 0x5ef ( readonly flush fua trim zeroes df multi cache )
flags: 0x58f ( readonly flush fua df multi cache )
min block: 1
opt block: 4096
max block: 33554432
Expand Down

0 comments on commit dbb38ca

Please sign in to comment.