Blame SOURCES/kvm-nbd-client-Support-qemu-img-convert-from-unaligned-s.patch

7711c0
From c443306c82400c6e04fe4af771552e88abb469a8 Mon Sep 17 00:00:00 2001
7711c0
From: John Snow <jsnow@redhat.com>
7711c0
Date: Mon, 6 May 2019 17:56:21 +0200
7711c0
Subject: [PATCH 11/53] nbd/client: Support qemu-img convert from unaligned
7711c0
 size
7711c0
7711c0
RH-Author: John Snow <jsnow@redhat.com>
7711c0
Message-id: <20190506175629.11079-12-jsnow@redhat.com>
7711c0
Patchwork-id: 87184
7711c0
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 11/19] nbd/client: Support qemu-img convert from unaligned size
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
If an NBD server advertises a size that is not a multiple of a sector,
7711c0
the block layer rounds up that size, even though we set info.size to
7711c0
the exact byte value sent by the server. The block layer then proceeds
7711c0
to let us read or query block status on the hole that it added past
7711c0
EOF, which the NBD server is unlikely to be happy with. Fortunately,
7711c0
qemu as a server never advertizes an unaligned size, so we generally
7711c0
don't run into this problem; but the nbdkit server makes it easy to
7711c0
test:
7711c0
7711c0
$ printf %1000d 1 > f1
7711c0
$ ~/nbdkit/nbdkit -fv file f1 & pid=$!
7711c0
$ qemu-img convert -f raw nbd://localhost:10809 f2
7711c0
$ kill $pid
7711c0
$ qemu-img compare f1 f2
7711c0
7711c0
Pre-patch, the server attempts a 1024-byte read, which nbdkit
7711c0
rightfully rejects as going beyond its advertised 1000 byte size; the
7711c0
conversion fails and the output files differ (not even the first
7711c0
sector is copied, because qemu-img does not follow ddrescue's habit of
7711c0
trying smaller reads to get as much information as possible in spite
7711c0
of errors). Post-patch, the client's attempts to read (and query block
7711c0
status, for new enough nbdkit) are properly truncated to the server's
7711c0
length, with sane handling of the hole the block layer forced on
7711c0
us. Although f2 ends up as a larger file (1024 bytes instead of 1000),
7711c0
qemu-img compare shows the two images to have identical contents for
7711c0
display to the guest.
7711c0
7711c0
I didn't add iotests coverage since I didn't want to add a dependency
7711c0
on nbdkit in iotests. I also did NOT patch write, trim, or write
7711c0
zeroes - these commands continue to fail (usually with ENOSPC, but
7711c0
whatever the server chose), because we really can't write to the end
7711c0
of the file, and because 'qemu-img convert' is the most common case
7711c0
where we care about being tolerant (which is read-only). Perhaps we
7711c0
could truncate the request if the client is writing zeros to the tail,
7711c0
but that seems like more work, especially if the block layer is fixed
7711c0
in 4.1 to track byte-accurate sizing (in which case this patch would
7711c0
be reverted as unnecessary).
7711c0
7711c0
Signed-off-by: Eric Blake <eblake@redhat.com>
7711c0
Message-Id: <20190329042750.14704-5-eblake@redhat.com>
7711c0
Tested-by: Richard W.M. Jones <rjones@redhat.com>
7711c0
(cherry picked from commit 9cf638508c0090b33ada4155c7cbb684e08e5ee9)
7711c0
Signed-off-by: John Snow <jsnow@redhat.com>
7711c0
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
7711c0
---
7711c0
 block/nbd-client.c | 39 ++++++++++++++++++++++++++++++++++++++-
7711c0
 1 file changed, 38 insertions(+), 1 deletion(-)
7711c0
7711c0
diff --git a/block/nbd-client.c b/block/nbd-client.c
7711c0
index 9b5779f..a1a84a8 100644
7711c0
--- a/block/nbd-client.c
7711c0
+++ b/block/nbd-client.c
7711c0
@@ -835,6 +835,25 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
7711c0
     if (!bytes) {
7711c0
         return 0;
7711c0
     }
7711c0
+    /*
7711c0
+     * Work around the fact that the block layer doesn't do
7711c0
+     * byte-accurate sizing yet - if the read exceeds the server's
7711c0
+     * advertised size because the block layer rounded size up, then
7711c0
+     * truncate the request to the server and tail-pad with zero.
7711c0
+     */
7711c0
+    if (offset >= client->info.size) {
7711c0
+        assert(bytes < BDRV_SECTOR_SIZE);
7711c0
+        qemu_iovec_memset(qiov, 0, 0, bytes);
7711c0
+        return 0;
7711c0
+    }
7711c0
+    if (offset + bytes > client->info.size) {
7711c0
+        uint64_t slop = offset + bytes - client->info.size;
7711c0
+
7711c0
+        assert(slop < BDRV_SECTOR_SIZE);
7711c0
+        qemu_iovec_memset(qiov, bytes - slop, 0, slop);
7711c0
+        request.len -= slop;
7711c0
+    }
7711c0
+
7711c0
     ret = nbd_co_send_request(bs, &request, NULL);
7711c0
     if (ret < 0) {
7711c0
         return ret;
7711c0
@@ -953,7 +972,8 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
7711c0
         .from = offset,
7711c0
         .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
7711c0
                                                 bs->bl.request_alignment),
7711c0
-                                client->info.max_block), bytes),
7711c0
+                                client->info.max_block),
7711c0
+                   MIN(bytes, client->info.size - offset)),
7711c0
         .flags = NBD_CMD_FLAG_REQ_ONE,
7711c0
     };
7711c0
 
7711c0
@@ -964,6 +984,23 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
7711c0
         return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
7711c0
     }
7711c0
 
7711c0
+    /*
7711c0
+     * Work around the fact that the block layer doesn't do
7711c0
+     * byte-accurate sizing yet - if the status request exceeds the
7711c0
+     * server's advertised size because the block layer rounded size
7711c0
+     * up, we truncated the request to the server (above), or are
7711c0
+     * called on just the hole.
7711c0
+     */
7711c0
+    if (offset >= client->info.size) {
7711c0
+        *pnum = bytes;
7711c0
+        assert(bytes < BDRV_SECTOR_SIZE);
7711c0
+        /* Intentionally don't report offset_valid for the hole */
7711c0
+        return BDRV_BLOCK_ZERO;
7711c0
+    }
7711c0
+
7711c0
+    if (client->info.min_block) {
7711c0
+        assert(QEMU_IS_ALIGNED(request.len, client->info.min_block));
7711c0
+    }
7711c0
     ret = nbd_co_send_request(bs, &request, NULL);
7711c0
     if (ret < 0) {
7711c0
         return ret;
7711c0
-- 
7711c0
1.8.3.1
7711c0