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

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