yeahuh / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone

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

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