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

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