From 65529782f8fec531fd114cc0f58399e88202153c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 7 Mar 2018 16:57:32 -0600 Subject: [PATCH] nbd/server: Honor FUA request on NBD_CMD_TRIM The NBD spec states that since trim requests can affect disk contents, then they should allow for FUA semantics just like writes for ensuring the disk has settled before returning. As bdrv_[co_]pdiscard() does not support a flags argument, we can't pass FUA down the block layer stack, and must therefore emulate it with a flush at the NBD layer. Note that in all reality, generic well-behaved clients will never send TRIM+FUA (in fact, qemu as a client never does, and we have no intention to plumb flags into bdrv_pdiscard). This is because the NBD protocol states that it is unspecified to READ a trimmed area (you might read stale data, all zeroes, or even random unrelated data) without first rewriting it, and even the experimental BLOCK_STATUS extension states that TRIM need not affect reported status. Thus, in the general case, a client cannot tell the difference between an arbitrary server that ignores TRIM, a server that had a power outage without flushing to disk, and a server that actually affected the disk before returning; so waiting for the trim actions to flush to disk makes little sense. However, for a specific client and server pair, where the client knows the server treats TRIM'd areas as guaranteed reads-zero, waiting for a flush makes sense, hence why the protocol documents that FUA is valid on trim. So, even though the NBD protocol doesn't have a way for the server to advertise what effects (if any) TRIM will actually have, and thus any client that relies on specific effects is probably in error, we can at least support a client that requests TRIM+FUA. Signed-off-by: Eric Blake Message-Id: <20180307225732.155835-1-eblake@redhat.com> --- nbd/server.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nbd/server.c b/nbd/server.c index 83f21c5591..4776009a2d 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1638,6 +1638,9 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, case NBD_CMD_TRIM: ret = blk_co_pdiscard(exp->blk, request->from + exp->dev_offset, request->len); + if (ret == 0 && request->flags & NBD_CMD_FLAG_FUA) { + ret = blk_co_flush(exp->blk); + } return nbd_send_generic_reply(client, request->handle, ret, "discard failed", errp);