03.04.2019 6:05, Eric Blake wrote:
> We've recently added traces for clients to flag server non-compliance;
> let's do the same for servers to flag client non-compliance. According
> to the spec, if the client requests NBD_INFO_BLOCK_SIZE, it is
> promising to send all requests aligned to those boundaries.  Of
> course, if the client does not request NBD_INFO_BLOCK_SIZE, then it
> made no promises so we shouldn't flag anything; and because we are
> willing to handle clients that made no promises (the spec allows us to
> use NBD_REP_ERR_BLOCK_SIZE_REQD if we had been unwilling), we already
> have to handle unaligned requests (which the block layer already does
> on our behalf).  So even though the spec allows us to return EINVAL
> for clients that promised to behave, it's easier to always answer
> unaligned requests.  Still, flagging non-compliance can be useful in
> debugging a client that is trying to be maximally portable.
> 
> Qemu as client used to have one spot where it sent non-compliant
> requests: if the server sends an unaligned reply to
> NBD_CMD_BLOCK_STATUS, and the client was iterating over the entire
> disk, the next request would start at that unaligned point; this was
> fixed in commit a39286dd when the client was taught to work around
> server non-compliance; but is equally fixed if the server is patched
> to not send unaligned replies in the first place (the next few patches
> will address that). Fortunately, I did not find any more spots where
> qemu as client was non-compliant. I was able to test the patch by
> using the following hack to convince qemu-io to run various unaligned
> commands, coupled with serving 512-byte alignment by intentionally
> omitting '-f raw' on the server while viewing server traces.
> 
> | diff --git i/nbd/client.c w/nbd/client.c
> | index 427980bdd22..1858b2aac35 100644
> | --- i/nbd/client.c
> | +++ w/nbd/client.c
> | @@ -449,6 +449,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t 
> opt,
> |                  nbd_send_opt_abort(ioc);
> |                  return -1;
> |              }
> | +            info->min_block = 1;//hack
> |              if (!is_power_of_2(info->min_block)) {
> |                  error_setg(errp, "server minimum block size %" PRIu32
> |                             " is not a power of two", info->min_block);
> 
> Signed-off-by: Eric Blake <ebl...@redhat.com>
> ---
>   nbd/server.c     | 12 ++++++++++++
>   nbd/trace-events |  1 +
>   2 files changed, 13 insertions(+)
> 
> diff --git a/nbd/server.c b/nbd/server.c
> index 1b8c8619896..3040ceb5606 100644
> --- a/nbd/server.c
> +++ b/nbd/server.c
> @@ -124,6 +124,8 @@ struct NBDClient {
>       int nb_requests;
>       bool closing;
> 
> +    uint32_t check_align; /* If non-zero, check for aligned client requests 
> */
> +
>       bool structured_reply;
>       NBDExportMetaContexts export_meta;
> 
> @@ -660,6 +662,8 @@ static int nbd_negotiate_handle_info(NBDClient *client, 
> uint16_t myflags,
> 
>       if (client->opt == NBD_OPT_GO) {
>           client->exp = exp;
> +        client->check_align = blocksize ?
> +            blk_get_request_alignment(exp->blk) : 0;

I think better set in same place, where sizes[0] set, or use sizes[0] here or 
add separate local
varibale for it, so that, if "sizes[0] =" changes somehow, we will not forget 
to fix this place too.

>           QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
>           nbd_export_get(client->exp);
>           nbd_check_meta_export(client);
> @@ -2126,6 +2130,14 @@ static int nbd_co_receive_request(NBDRequestData *req, 
> NBDRequest *request,
>           return (request->type == NBD_CMD_WRITE ||
>                   request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL;
>       }
> +    if (client->check_align && !QEMU_IS_ALIGNED(request->from | request->len,
> +                                                client->check_align)) {
> +        /*
> +         * The block layer gracefully handles unaligned requests, but
> +         * it's still worth tracing client non-compliance
> +         */
> +        trace_nbd_co_receive_align_compliance(nbd_cmd_lookup(request->type));
> +    }
>       valid_flags = NBD_CMD_FLAG_FUA;
>       if (request->type == NBD_CMD_READ && client->structured_reply) {
>           valid_flags |= NBD_CMD_FLAG_DF;
> diff --git a/nbd/trace-events b/nbd/trace-events
> index a6cca8fdf83..87a2b896c35 100644
> --- a/nbd/trace-events
> +++ b/nbd/trace-events
> @@ -71,4 +71,5 @@ nbd_co_send_extents(uint64_t handle, unsigned int extents, 
> uint32_t id, uint64_t
>   nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, 
> const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = 
> %d (%s), msg = '%s'"
>   nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const 
> char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)"
>   nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) 
> "Payload received: handle = %" PRIu64 ", len = %" PRIu32
> +nbd_co_receive_align_compliance(const char *op) "client sent non-compliant 
> unaligned %s request"

Don't you want print request->from, request->len and client->check_align as 
well?

>   nbd_trip(void) "Reading request"
> 

Patch seems correct anyway, so if you are in a hurry, it's OK as is:

Reviewed-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com>

-- 
Best regards,
Vladimir

Reply via email to