|
|
7711c0 |
From 557b85bcd4aedf4550b272abb57f817f7dc8eba1 Mon Sep 17 00:00:00 2001
|
|
|
7711c0 |
From: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Date: Mon, 6 May 2019 17:56:12 +0200
|
|
|
7711c0 |
Subject: [PATCH 02/53] nbd: Tolerate some server non-compliance in
|
|
|
7711c0 |
NBD_CMD_BLOCK_STATUS
|
|
|
7711c0 |
|
|
|
7711c0 |
RH-Author: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Message-id: <20190506175629.11079-3-jsnow@redhat.com>
|
|
|
7711c0 |
Patchwork-id: 87193
|
|
|
7711c0 |
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/19] nbd: Tolerate some server non-compliance in NBD_CMD_BLOCK_STATUS
|
|
|
7711c0 |
Bugzilla: 1692018
|
|
|
7711c0 |
RH-Acked-by: Max Reitz <mreitz@redhat.com>
|
|
|
7711c0 |
RH-Acked-by: Stefano Garzarella <sgarzare@redhat.com>
|
|
|
7711c0 |
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
7711c0 |
|
|
|
7711c0 |
From: Eric Blake <eblake@redhat.com>
|
|
|
7711c0 |
|
|
|
7711c0 |
The NBD spec states that NBD_CMD_FLAG_REQ_ONE (which we currently
|
|
|
7711c0 |
always use) should not reply with an extent larger than our request,
|
|
|
7711c0 |
and that the server's response should be exactly one extent. Right
|
|
|
7711c0 |
now, that means that if a server sends more than one extent, we treat
|
|
|
7711c0 |
the server as broken, fail the block status request, and disconnect,
|
|
|
7711c0 |
which prevents all further use of the block device. But while good
|
|
|
7711c0 |
software should be strict in what it sends, it should be tolerant in
|
|
|
7711c0 |
what it receives.
|
|
|
7711c0 |
|
|
|
7711c0 |
While trying to implement NBD_CMD_BLOCK_STATUS in nbdkit, we
|
|
|
7711c0 |
temporarily had a non-compliant server sending too many extents in
|
|
|
7711c0 |
spite of REQ_ONE. Oddly enough, 'qemu-img convert' with qemu 3.1
|
|
|
7711c0 |
failed with a somewhat useful message:
|
|
|
7711c0 |
qemu-img: Protocol error: invalid payload for NBD_REPLY_TYPE_BLOCK_STATUS
|
|
|
7711c0 |
|
|
|
7711c0 |
which then disappeared with commit d8b4bad8, on the grounds that an
|
|
|
7711c0 |
error message flagged only at the time of coroutine teardown is
|
|
|
7711c0 |
pointless, and instead we should rely on the actual failed API to
|
|
|
7711c0 |
report an error - in other words, the 3.1 behavior was masking the
|
|
|
7711c0 |
fact that qemu-img was not reporting an error. That has since been
|
|
|
7711c0 |
fixed in the previous patch, where qemu-img convert now fails with:
|
|
|
7711c0 |
qemu-img: error while reading block status of sector 0: Invalid argument
|
|
|
7711c0 |
|
|
|
7711c0 |
But even that is harsh. Since we already partially relaxed things in
|
|
|
7711c0 |
commit acfd8f7a to tolerate a server that exceeds the cap (although
|
|
|
7711c0 |
that change was made prior to the NBD spec actually putting a cap on
|
|
|
7711c0 |
the extent length during REQ_ONE - in fact, the NBD spec change was
|
|
|
7711c0 |
BECAUSE of the qemu behavior prior to that commit), it's not that much
|
|
|
7711c0 |
harder to argue that we should also tolerate a server that sends too
|
|
|
7711c0 |
many extents. But at the same time, it's nice to trace when we are
|
|
|
7711c0 |
being tolerant of server non-compliance, in order to help server
|
|
|
7711c0 |
writers fix their implementations to be more portable (if they refer
|
|
|
7711c0 |
to our traces, rather than just stderr).
|
|
|
7711c0 |
|
|
|
7711c0 |
Reported-by: Richard W.M. Jones <rjones@redhat.com>
|
|
|
7711c0 |
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
|
7711c0 |
Message-Id: <20190323212639.579-3-eblake@redhat.com>
|
|
|
7711c0 |
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
|
|
7711c0 |
(cherry picked from commit a39286dd61e455014694cb6aa44cfa9e5c86d101)
|
|
|
7711c0 |
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
|
7711c0 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
7711c0 |
---
|
|
|
7711c0 |
block/nbd-client.c | 21 ++++++++++++++++-----
|
|
|
7711c0 |
block/trace-events | 1 +
|
|
|
7711c0 |
2 files changed, 17 insertions(+), 5 deletions(-)
|
|
|
7711c0 |
|
|
|
7711c0 |
diff --git a/block/nbd-client.c b/block/nbd-client.c
|
|
|
7711c0 |
index 1230850..f3c31d1 100644
|
|
|
7711c0 |
--- a/block/nbd-client.c
|
|
|
7711c0 |
+++ b/block/nbd-client.c
|
|
|
7711c0 |
@@ -227,8 +227,8 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
/* nbd_parse_blockstatus_payload
|
|
|
7711c0 |
- * support only one extent in reply and only for
|
|
|
7711c0 |
- * base:allocation context
|
|
|
7711c0 |
+ * Based on our request, we expect only one extent in reply, for the
|
|
|
7711c0 |
+ * base:allocation context.
|
|
|
7711c0 |
*/
|
|
|
7711c0 |
static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
|
|
7711c0 |
NBDStructuredReplyChunk *chunk,
|
|
|
7711c0 |
@@ -237,7 +237,8 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
|
|
7711c0 |
{
|
|
|
7711c0 |
uint32_t context_id;
|
|
|
7711c0 |
|
|
|
7711c0 |
- if (chunk->length != sizeof(context_id) + sizeof(*extent)) {
|
|
|
7711c0 |
+ /* The server succeeded, so it must have sent [at least] one extent */
|
|
|
7711c0 |
+ if (chunk->length < sizeof(context_id) + sizeof(*extent)) {
|
|
|
7711c0 |
error_setg(errp, "Protocol error: invalid payload for "
|
|
|
7711c0 |
"NBD_REPLY_TYPE_BLOCK_STATUS");
|
|
|
7711c0 |
return -EINVAL;
|
|
|
7711c0 |
@@ -263,10 +264,20 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
|
|
7711c0 |
return -EINVAL;
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
- /* The server is allowed to send us extra information on the final
|
|
|
7711c0 |
- * extent; just clamp it to the length we requested. */
|
|
|
7711c0 |
+ /*
|
|
|
7711c0 |
+ * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have
|
|
|
7711c0 |
+ * sent us any more than one extent, nor should it have included
|
|
|
7711c0 |
+ * status beyond our request in that extent. However, it's easy
|
|
|
7711c0 |
+ * enough to ignore the server's noncompliance without killing the
|
|
|
7711c0 |
+ * connection; just ignore trailing extents, and clamp things to
|
|
|
7711c0 |
+ * the length of our request.
|
|
|
7711c0 |
+ */
|
|
|
7711c0 |
+ if (chunk->length > sizeof(context_id) + sizeof(*extent)) {
|
|
|
7711c0 |
+ trace_nbd_parse_blockstatus_compliance("more than one extent");
|
|
|
7711c0 |
+ }
|
|
|
7711c0 |
if (extent->length > orig_length) {
|
|
|
7711c0 |
extent->length = orig_length;
|
|
|
7711c0 |
+ trace_nbd_parse_blockstatus_compliance("extent length too large");
|
|
|
7711c0 |
}
|
|
|
7711c0 |
|
|
|
7711c0 |
return 0;
|
|
|
7711c0 |
diff --git a/block/trace-events b/block/trace-events
|
|
|
7711c0 |
index 6d4d399..59c6f54 100644
|
|
|
7711c0 |
--- a/block/trace-events
|
|
|
7711c0 |
+++ b/block/trace-events
|
|
|
7711c0 |
@@ -152,5 +152,6 @@ nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64
|
|
|
7711c0 |
nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d"
|
|
|
7711c0 |
|
|
|
7711c0 |
# block/nbd-client.c
|
|
|
7711c0 |
+nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s"
|
|
|
7711c0 |
nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s"
|
|
|
7711c0 |
nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s"
|
|
|
7711c0 |
--
|
|
|
7711c0 |
1.8.3.1
|
|
|
7711c0 |
|