Enable QXL device build
Enable building for ppc64le Re-added Spice support Don't remove slof.bin for ppc64le
This commit is contained in:
commit
b6f23ffb3c
167
kvm-block-io_uring-avoid-potentially-getting-stuck-after.patch
Normal file
167
kvm-block-io_uring-avoid-potentially-getting-stuck-after.patch
Normal file
@ -0,0 +1,167 @@
|
||||
From 4ad89aa6a9efcbc0420e332acab2dd06e55be2fa Mon Sep 17 00:00:00 2001
|
||||
From: Fiona Ebner <f.ebner@proxmox.com>
|
||||
Date: Tue, 25 Nov 2025 14:31:03 +0100
|
||||
Subject: [PATCH 3/4] block/io_uring: avoid potentially getting stuck after
|
||||
resubmit at the end of ioq_submit()
|
||||
|
||||
RH-Author: Hanna Czenczek <hreitz@redhat.com>
|
||||
RH-MergeRequest: 479: linux-aio/io-uring: Resubmit tails of short requests
|
||||
RH-Jira: RHEL-158224
|
||||
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Commit: [3/4] fd599da3ffcd6b37ceff35587ae9dbc1698b0f57 (hreitz/qemu-kvm-c-9-s)
|
||||
|
||||
Note that this issue seems already fixed as a consequence of the large
|
||||
io_uring rework with 047dabef97 ("block/io_uring: use aio_add_sqe()")
|
||||
in current master, so this is purely for QEMU stable branches.
|
||||
|
||||
At the end of ioq_submit(), there is an opportunistic call to
|
||||
luring_process_completions(). This is the single caller of
|
||||
luring_process_completions() that doesn't use the
|
||||
luring_process_completions_and_submit() wrapper.
|
||||
|
||||
Other callers use the wrapper, because luring_process_completions()
|
||||
might require a subsequent call to ioq_submit() after resubmitting a
|
||||
request. As noted for luring_resubmit():
|
||||
|
||||
> Resubmit a request by appending it to submit_queue. The caller must ensure
|
||||
> that ioq_submit() is called later so that submit_queue requests are started.
|
||||
|
||||
So the caller at the end of ioq_submit() violates the contract and can
|
||||
in fact be problematic if no other requests come in later. In such a
|
||||
case, the request intended to be resubmitted will never be actually be
|
||||
submitted via io_uring_submit().
|
||||
|
||||
A reproducer exposing this issue is [0], which is based on user
|
||||
reports from [1]. Another reproducer is iotest 109 with '-i io_uring'.
|
||||
|
||||
I had the most success to trigger the issue with [0] when using a
|
||||
BTRFS RAID 1 storage. With tmpfs, it can take quite a few iterations,
|
||||
but also triggers eventually on my machine. With iotest 109 with '-i
|
||||
io_uring' the issue triggers reliably on my ext4 file system.
|
||||
|
||||
Have ioq_submit() submit any resubmitted requests after calling
|
||||
luring_process_completions(). The return value from io_uring_submit()
|
||||
is checked to be non-negative before the opportunistic processing of
|
||||
completions and going for the new resubmit logic, to ensure that a
|
||||
failure of io_uring_submit() is not missed. Also note that the return
|
||||
value already was not necessarily the total number of submissions,
|
||||
since the loop might've been iterated more than once even before the
|
||||
current change.
|
||||
|
||||
Only trigger the resubmission logic if it is actually necessary to
|
||||
avoid changing behavior more than necessary. For example iotest 109
|
||||
would produce more 'mirror ready' events if always resubmitting after
|
||||
luring_process_completions() at the end of ioq_submit().
|
||||
|
||||
Note iotest 109 still does not pass as is when run with '-i io_uring',
|
||||
because of two offset values for BLOCK_JOB_COMPLETED events being zero
|
||||
instead of non-zero as in the expected output. Note that the two
|
||||
affected test cases are expected failures and still fail, so they just
|
||||
fail "faster". The test cases are actually not triggering the resubmit
|
||||
logic, so the reason seems to be different ordering of requests and
|
||||
completions of the current aio=io_uring implementation versus
|
||||
aio=threads.
|
||||
|
||||
[0]:
|
||||
|
||||
> #!/bin/bash -e
|
||||
> #file=/mnt/btrfs/disk.raw
|
||||
> file=/tmp/disk.raw
|
||||
> filesize=256
|
||||
> readsize=512
|
||||
> rm -f $file
|
||||
> truncate -s $filesize $file
|
||||
> ./qemu-system-x86_64 --trace '*uring*' --qmp stdio \
|
||||
> --blockdev raw,node-name=node0,file.driver=file,file.cache.direct=off,file.filename=$file,file.aio=io_uring \
|
||||
> <<EOF
|
||||
> {"execute": "qmp_capabilities"}
|
||||
> {"execute": "human-monitor-command", "arguments": { "command-line": "qemu-io node0 \"read 0 $readsize \"" }}
|
||||
> {"execute": "quit"}
|
||||
> EOF
|
||||
|
||||
[1]: https://forum.proxmox.com/threads/170045/
|
||||
|
||||
Cc: qemu-stable@nongnu.org
|
||||
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
|
||||
(cherry picked from commit 2bb0153cd806b8f6b4f82b353bd0113cd1c488a5)
|
||||
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
---
|
||||
block/io_uring.c | 16 +++++++++++++---
|
||||
1 file changed, 13 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/block/io_uring.c b/block/io_uring.c
|
||||
index dd4f304910..5dbafc8f7b 100644
|
||||
--- a/block/io_uring.c
|
||||
+++ b/block/io_uring.c
|
||||
@@ -120,11 +120,14 @@ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb,
|
||||
* event loop. When there are no events left to complete the BH is being
|
||||
* canceled.
|
||||
*
|
||||
+ * Returns whether ioq_submit() must be called again afterwards since requests
|
||||
+ * were resubmitted via luring_resubmit().
|
||||
*/
|
||||
-static void luring_process_completions(LuringState *s)
|
||||
+static bool luring_process_completions(LuringState *s)
|
||||
{
|
||||
struct io_uring_cqe *cqes;
|
||||
int total_bytes;
|
||||
+ bool resubmit = false;
|
||||
|
||||
defer_call_begin();
|
||||
|
||||
@@ -182,6 +185,7 @@ static void luring_process_completions(LuringState *s)
|
||||
*/
|
||||
if (ret == -EINTR || ret == -EAGAIN) {
|
||||
luring_resubmit(s, luringcb);
|
||||
+ resubmit = true;
|
||||
continue;
|
||||
}
|
||||
} else if (!luringcb->qiov) {
|
||||
@@ -194,6 +198,7 @@ static void luring_process_completions(LuringState *s)
|
||||
if (luringcb->is_read) {
|
||||
if (ret > 0) {
|
||||
luring_resubmit_short_read(s, luringcb, ret);
|
||||
+ resubmit = true;
|
||||
continue;
|
||||
} else {
|
||||
/* Pad with zeroes */
|
||||
@@ -224,6 +229,8 @@ end:
|
||||
qemu_bh_cancel(s->completion_bh);
|
||||
|
||||
defer_call_end();
|
||||
+
|
||||
+ return resubmit;
|
||||
}
|
||||
|
||||
static int ioq_submit(LuringState *s)
|
||||
@@ -231,6 +238,7 @@ static int ioq_submit(LuringState *s)
|
||||
int ret = 0;
|
||||
LuringAIOCB *luringcb, *luringcb_next;
|
||||
|
||||
+resubmit:
|
||||
while (s->io_q.in_queue > 0) {
|
||||
/*
|
||||
* Try to fetch sqes from the ring for requests waiting in
|
||||
@@ -260,12 +268,14 @@ static int ioq_submit(LuringState *s)
|
||||
}
|
||||
s->io_q.blocked = (s->io_q.in_queue > 0);
|
||||
|
||||
- if (s->io_q.in_flight) {
|
||||
+ if (ret >= 0 && s->io_q.in_flight) {
|
||||
/*
|
||||
* We can try to complete something just right away if there are
|
||||
* still requests in-flight.
|
||||
*/
|
||||
- luring_process_completions(s);
|
||||
+ if (luring_process_completions(s)) {
|
||||
+ goto resubmit;
|
||||
+ }
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
--
|
||||
2.47.3
|
||||
|
||||
278
kvm-io-uring-Resubmit-tails-of-short-writes.patch
Normal file
278
kvm-io-uring-Resubmit-tails-of-short-writes.patch
Normal file
@ -0,0 +1,278 @@
|
||||
From 1bdac2a8c4ac77133cb0c2b4d40819bff1a35fc4 Mon Sep 17 00:00:00 2001
|
||||
From: Hanna Czenczek <hreitz@redhat.com>
|
||||
Date: Tue, 24 Mar 2026 09:43:36 +0100
|
||||
Subject: [PATCH 4/4] io-uring: Resubmit tails of short writes
|
||||
|
||||
RH-Author: Hanna Czenczek <hreitz@redhat.com>
|
||||
RH-MergeRequest: 479: linux-aio/io-uring: Resubmit tails of short requests
|
||||
RH-Jira: RHEL-158224
|
||||
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Commit: [4/4] 7ff66622acbb5dbdbacafe71bffd0a277ac919d9 (hreitz/qemu-kvm-c-9-s)
|
||||
|
||||
Short writes can happen, too, not just short reads. The difference to
|
||||
aio=native is that the kernel will actually retry the tail of short
|
||||
requests internally already -- so it is harder to reproduce. But if the
|
||||
tail of a short request returns an error to the kernel, we will see it
|
||||
in userspace still. To reproduce this, apply the following patch on top
|
||||
of the one shown in HEAD^ (again %s/escaped // to apply):
|
||||
|
||||
escaped diff --git a/block/export/fuse.c b/block/export/fuse.c
|
||||
escaped index 67dc50a412..2b98489a32 100644
|
||||
escaped --- a/block/export/fuse.c
|
||||
escaped +++ b/block/export/fuse.c
|
||||
@@ -1059,8 +1059,15 @@ fuse_co_read(FuseExport *exp, void **bufptr, uint64_t offset, uint32_t size)
|
||||
int64_t blk_len;
|
||||
void *buf;
|
||||
int ret;
|
||||
+ static uint32_t error_size;
|
||||
|
||||
- size = MIN(size, 4096);
|
||||
+ if (error_size == size) {
|
||||
+ error_size = 0;
|
||||
+ return -EIO;
|
||||
+ } else if (size > 4096) {
|
||||
+ error_size = size - 4096;
|
||||
+ size = 4096;
|
||||
+ }
|
||||
|
||||
/* Limited by max_read, should not happen */
|
||||
if (size > FUSE_MAX_READ_BYTES) {
|
||||
@@ -1111,8 +1118,15 @@ fuse_co_write(FuseExport *exp, struct fuse_write_out *out,
|
||||
{
|
||||
int64_t blk_len;
|
||||
int ret;
|
||||
+ static uint32_t error_size;
|
||||
|
||||
- size = MIN(size, 4096);
|
||||
+ if (error_size == size) {
|
||||
+ error_size = 0;
|
||||
+ return -EIO;
|
||||
+ } else if (size > 4096) {
|
||||
+ error_size = size - 4096;
|
||||
+ size = 4096;
|
||||
+ }
|
||||
|
||||
QEMU_BUILD_BUG_ON(FUSE_MAX_WRITE_BYTES > BDRV_REQUEST_MAX_BYTES);
|
||||
/* Limited by max_write, should not happen */
|
||||
|
||||
I know this is a bit artificial because to produce this, there must be
|
||||
an I/O error somewhere anyway, but if it does happen, qemu will
|
||||
understand it to mean ENOSPC for short writes, which is incorrect. So I
|
||||
believe we need to resubmit the tail to maybe have it succeed now, or at
|
||||
least get the correct error code.
|
||||
|
||||
Reproducer as before:
|
||||
$ ./qemu-img create -f raw test.raw 8k
|
||||
Formatting 'test.raw', fmt=raw size=8192
|
||||
$ ./qemu-io -f raw -c 'write -P 42 0 8k' test.raw
|
||||
wrote 8192/8192 bytes at offset 0
|
||||
8 KiB, 1 ops; 00.00 sec (64.804 MiB/sec and 8294.9003 ops/sec)
|
||||
$ hexdump -C test.raw
|
||||
00000000 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |****************|
|
||||
*
|
||||
00002000
|
||||
$ storage-daemon/qemu-storage-daemon \
|
||||
--blockdev file,node-name=test,filename=test.raw \
|
||||
--export fuse,id=exp,node-name=test,mountpoint=test.raw,writable=true
|
||||
|
||||
$ ./qemu-io --image-opts -c 'read -P 23 0 8k' \
|
||||
driver=file,filename=test.raw,cache.direct=on,aio=io_uring
|
||||
read 8192/8192 bytes at offset 0
|
||||
8 KiB, 1 ops; 00.00 sec (58.481 MiB/sec and 7485.5342 ops/sec)
|
||||
$ ./qemu-io --image-opts -c 'write -P 23 0 8k' \
|
||||
driver=file,filename=test.raw,cache.direct=on,aio=io_uring
|
||||
write failed: No space left on device
|
||||
$ hexdump -C test.raw
|
||||
00000000 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 |................|
|
||||
*
|
||||
00001000 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |****************|
|
||||
*
|
||||
00002000
|
||||
|
||||
So short reads already work (because there is code for that), but short
|
||||
writes incorrectly produce ENOSPC. This patch fixes that by
|
||||
resubmitting not only the tail of short reads but short writes also.
|
||||
|
||||
(And this patch uses the opportunity to make it so qemu_iovec_destroy()
|
||||
is called only if req->resubmit_qiov.iov is non-NULL. Functionally a
|
||||
non-op, but this is how the code generally checks whether the
|
||||
resubmit_qiov has been set up or not.)
|
||||
|
||||
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
Message-ID: <20260324084338.37453-4-hreitz@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
(cherry picked from commit cf9cdaea6e24d13dfdf8402f6829d2ca4dca864b)
|
||||
|
||||
Conflicts:
|
||||
- block/io_uring.c, block/trace-events
|
||||
Missing 047dabef97bd0c4af3c3dc453b19e20345de3602 ("block/io_uring: use
|
||||
aio_add_sqe()") downstream, which changed quite a few things about the
|
||||
io-uring code. Backporting it seems excessive, though, especially
|
||||
given that it would pull in even more dependencies (general aio and
|
||||
aio-posix changes that come right before 047dabef).
|
||||
|
||||
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
---
|
||||
block/io_uring.c | 77 +++++++++++++++++++++++++---------------------
|
||||
block/trace-events | 2 +-
|
||||
2 files changed, 43 insertions(+), 36 deletions(-)
|
||||
|
||||
diff --git a/block/io_uring.c b/block/io_uring.c
|
||||
index 5dbafc8f7b..582550f8e9 100644
|
||||
--- a/block/io_uring.c
|
||||
+++ b/block/io_uring.c
|
||||
@@ -31,14 +31,14 @@ typedef struct LuringAIOCB {
|
||||
struct io_uring_sqe sqeq;
|
||||
ssize_t ret;
|
||||
QEMUIOVector *qiov;
|
||||
- bool is_read;
|
||||
+ int type;
|
||||
QSIMPLEQ_ENTRY(LuringAIOCB) next;
|
||||
|
||||
/*
|
||||
- * Buffered reads may require resubmission, see
|
||||
- * luring_resubmit_short_read().
|
||||
+ * Short reads/writes require resubmission, see
|
||||
+ * luring_resubmit_short_io().
|
||||
*/
|
||||
- int total_read;
|
||||
+ int total_done;
|
||||
QEMUIOVector resubmit_qiov;
|
||||
} LuringAIOCB;
|
||||
|
||||
@@ -73,22 +73,27 @@ static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb)
|
||||
}
|
||||
|
||||
/**
|
||||
- * luring_resubmit_short_read:
|
||||
+ * luring_resubmit_short_io:
|
||||
*
|
||||
- * Short reads are rare but may occur. The remaining read request needs to be
|
||||
- * resubmitted.
|
||||
+ * Short reads and writes are rare but may occur. The remaining request needs
|
||||
+ * to be resubmitted.
|
||||
+ *
|
||||
+ * For example, short reads can be reproduced by a FUSE export deliberately
|
||||
+ * executing short reads. The tail of short writes is generally resubmitted by
|
||||
+ * io-uring in the kernel, but if that resubmission encounters an I/O error, the
|
||||
+ * already submitted portion will be returned as a short write.
|
||||
*/
|
||||
-static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb,
|
||||
- int nread)
|
||||
+static void luring_resubmit_short_io(LuringState *s, LuringAIOCB *luringcb,
|
||||
+ int ndone)
|
||||
{
|
||||
QEMUIOVector *resubmit_qiov;
|
||||
size_t remaining;
|
||||
|
||||
- trace_luring_resubmit_short_read(s, luringcb, nread);
|
||||
+ trace_luring_resubmit_short_io(s, luringcb, ndone);
|
||||
|
||||
- /* Update read position */
|
||||
- luringcb->total_read += nread;
|
||||
- remaining = luringcb->qiov->size - luringcb->total_read;
|
||||
+ /* Update I/O position */
|
||||
+ luringcb->total_done += ndone;
|
||||
+ remaining = luringcb->qiov->size - luringcb->total_done;
|
||||
|
||||
/* Shorten qiov */
|
||||
resubmit_qiov = &luringcb->resubmit_qiov;
|
||||
@@ -97,11 +102,11 @@ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb,
|
||||
} else {
|
||||
qemu_iovec_reset(resubmit_qiov);
|
||||
}
|
||||
- qemu_iovec_concat(resubmit_qiov, luringcb->qiov, luringcb->total_read,
|
||||
+ qemu_iovec_concat(resubmit_qiov, luringcb->qiov, luringcb->total_done,
|
||||
remaining);
|
||||
|
||||
/* Update sqe */
|
||||
- luringcb->sqeq.off += nread;
|
||||
+ luringcb->sqeq.off += ndone;
|
||||
luringcb->sqeq.addr = (uintptr_t)luringcb->resubmit_qiov.iov;
|
||||
luringcb->sqeq.len = luringcb->resubmit_qiov.niov;
|
||||
|
||||
@@ -165,8 +170,8 @@ static bool luring_process_completions(LuringState *s)
|
||||
s->io_q.in_flight--;
|
||||
trace_luring_process_completion(s, luringcb, ret);
|
||||
|
||||
- /* total_read is non-zero only for resubmitted read requests */
|
||||
- total_bytes = ret + luringcb->total_read;
|
||||
+ /* total_done is non-zero only for resubmitted requests */
|
||||
+ total_bytes = ret + luringcb->total_done;
|
||||
|
||||
if (ret < 0) {
|
||||
/*
|
||||
@@ -192,27 +197,29 @@ static bool luring_process_completions(LuringState *s)
|
||||
goto end;
|
||||
} else if (total_bytes == luringcb->qiov->size) {
|
||||
ret = 0;
|
||||
- /* Only read/write */
|
||||
+ } else if (ret > 0 && (luringcb->type == QEMU_AIO_READ ||
|
||||
+ luringcb->type == QEMU_AIO_WRITE)) {
|
||||
+ luring_resubmit_short_io(s, luringcb, ret);
|
||||
+ resubmit = true;
|
||||
+ continue;
|
||||
+ } else if (luringcb->type == QEMU_AIO_READ) {
|
||||
+ /* Read ret == 0: EOF, pad with zeroes */
|
||||
+ qemu_iovec_memset(luringcb->qiov, total_bytes, 0,
|
||||
+ luringcb->qiov->size - total_bytes);
|
||||
+ ret = 0;
|
||||
} else {
|
||||
- /* Short Read/Write */
|
||||
- if (luringcb->is_read) {
|
||||
- if (ret > 0) {
|
||||
- luring_resubmit_short_read(s, luringcb, ret);
|
||||
- resubmit = true;
|
||||
- continue;
|
||||
- } else {
|
||||
- /* Pad with zeroes */
|
||||
- qemu_iovec_memset(luringcb->qiov, total_bytes, 0,
|
||||
- luringcb->qiov->size - total_bytes);
|
||||
- ret = 0;
|
||||
- }
|
||||
- } else {
|
||||
- ret = -ENOSPC;
|
||||
- }
|
||||
+ /*
|
||||
+ * Normal write ret == 0 means ENOSPC.
|
||||
+ * For zone-append, we treat any 0 <= ret < qiov->size as ENOSPC,
|
||||
+ * too, because resubmitting the tail seems a little unsafe.
|
||||
+ */
|
||||
+ ret = -ENOSPC;
|
||||
}
|
||||
end:
|
||||
luringcb->ret = ret;
|
||||
- qemu_iovec_destroy(&luringcb->resubmit_qiov);
|
||||
+ if (luringcb->resubmit_qiov.iov) {
|
||||
+ qemu_iovec_destroy(&luringcb->resubmit_qiov);
|
||||
+ }
|
||||
|
||||
/*
|
||||
* If the coroutine is already entered it must be in ioq_submit()
|
||||
@@ -409,7 +416,7 @@ int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset,
|
||||
.co = qemu_coroutine_self(),
|
||||
.ret = -EINPROGRESS,
|
||||
.qiov = qiov,
|
||||
- .is_read = (type == QEMU_AIO_READ),
|
||||
+ .type = type,
|
||||
};
|
||||
trace_luring_co_submit(bs, s, &luringcb, fd, offset, qiov ? qiov->size : 0,
|
||||
type);
|
||||
diff --git a/block/trace-events b/block/trace-events
|
||||
index 8e789e1f12..99b8c12bc8 100644
|
||||
--- a/block/trace-events
|
||||
+++ b/block/trace-events
|
||||
@@ -70,7 +70,7 @@ luring_do_submit_done(void *s, int ret) "LuringState %p submitted to kernel %d"
|
||||
luring_co_submit(void *bs, void *s, void *luringcb, int fd, uint64_t offset, size_t nbytes, int type) "bs %p s %p luringcb %p fd %d offset %" PRId64 " nbytes %zd type %d"
|
||||
luring_process_completion(void *s, void *aiocb, int ret) "LuringState %p luringcb %p ret %d"
|
||||
luring_io_uring_submit(void *s, int ret) "LuringState %p ret %d"
|
||||
-luring_resubmit_short_read(void *s, void *luringcb, int nread) "LuringState %p luringcb %p nread %d"
|
||||
+luring_resubmit_short_io(void *s, void *luringcb, int ndone) "LuringState %p luringcb %p ndone %d"
|
||||
|
||||
# qcow2.c
|
||||
qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t host_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu"
|
||||
--
|
||||
2.47.3
|
||||
|
||||
127
kvm-linux-aio-Put-all-parameters-into-qemu_laiocb.patch
Normal file
127
kvm-linux-aio-Put-all-parameters-into-qemu_laiocb.patch
Normal file
@ -0,0 +1,127 @@
|
||||
From 30c3962bbef6ce083f326988f35741dd21aaf09d Mon Sep 17 00:00:00 2001
|
||||
From: Hanna Czenczek <hreitz@redhat.com>
|
||||
Date: Tue, 24 Mar 2026 09:43:34 +0100
|
||||
Subject: [PATCH 1/4] linux-aio: Put all parameters into qemu_laiocb
|
||||
|
||||
RH-Author: Hanna Czenczek <hreitz@redhat.com>
|
||||
RH-MergeRequest: 479: linux-aio/io-uring: Resubmit tails of short requests
|
||||
RH-Jira: RHEL-158224
|
||||
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Commit: [1/4] f449ea0c49a093bbec59b24fb44308e7f58c9ed2 (hreitz/qemu-kvm-c-9-s)
|
||||
|
||||
Put all request parameters into the qemu_laiocb struct, which will allow
|
||||
re-submitting the tail of short reads/writes.
|
||||
|
||||
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
Message-ID: <20260324084338.37453-2-hreitz@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
(cherry picked from commit cc03b62df47a09c507e199cc043f57bdc941cc67)
|
||||
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
---
|
||||
block/linux-aio.c | 34 ++++++++++++++++++++++------------
|
||||
1 file changed, 22 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/block/linux-aio.c b/block/linux-aio.c
|
||||
index c200e7ad20..c2c5e11946 100644
|
||||
--- a/block/linux-aio.c
|
||||
+++ b/block/linux-aio.c
|
||||
@@ -41,9 +41,15 @@ struct qemu_laiocb {
|
||||
LinuxAioState *ctx;
|
||||
struct iocb iocb;
|
||||
ssize_t ret;
|
||||
+ off_t offset;
|
||||
size_t nbytes;
|
||||
QEMUIOVector *qiov;
|
||||
- bool is_read;
|
||||
+
|
||||
+ int fd;
|
||||
+ int type;
|
||||
+ BdrvRequestFlags flags;
|
||||
+
|
||||
+ uint64_t dev_max_batch;
|
||||
QSIMPLEQ_ENTRY(qemu_laiocb) next;
|
||||
};
|
||||
|
||||
@@ -87,7 +93,7 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
|
||||
ret = 0;
|
||||
} else if (ret >= 0) {
|
||||
/* Short reads mean EOF, pad with zeros. */
|
||||
- if (laiocb->is_read) {
|
||||
+ if (laiocb->type == QEMU_AIO_READ) {
|
||||
qemu_iovec_memset(laiocb->qiov, ret, 0,
|
||||
laiocb->qiov->size - ret);
|
||||
} else {
|
||||
@@ -367,23 +373,23 @@ static void laio_deferred_fn(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
-static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
- int type, BdrvRequestFlags flags,
|
||||
- uint64_t dev_max_batch)
|
||||
+static int laio_do_submit(struct qemu_laiocb *laiocb)
|
||||
{
|
||||
LinuxAioState *s = laiocb->ctx;
|
||||
struct iocb *iocbs = &laiocb->iocb;
|
||||
QEMUIOVector *qiov = laiocb->qiov;
|
||||
+ int fd = laiocb->fd;
|
||||
+ off_t offset = laiocb->offset;
|
||||
|
||||
- switch (type) {
|
||||
+ switch (laiocb->type) {
|
||||
case QEMU_AIO_WRITE:
|
||||
#ifdef HAVE_IO_PREP_PWRITEV2
|
||||
{
|
||||
- int laio_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
|
||||
+ int laio_flags = (laiocb->flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
|
||||
io_prep_pwritev2(iocbs, fd, qiov->iov, qiov->niov, offset, laio_flags);
|
||||
}
|
||||
#else
|
||||
- assert(flags == 0);
|
||||
+ assert(laiocb->flags == 0);
|
||||
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
|
||||
#endif
|
||||
break;
|
||||
@@ -399,7 +405,7 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
/* Currently Linux kernel does not support other operations */
|
||||
default:
|
||||
fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
|
||||
- __func__, type);
|
||||
+ __func__, laiocb->type);
|
||||
return -EIO;
|
||||
}
|
||||
io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e));
|
||||
@@ -407,7 +413,7 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next);
|
||||
s->io_q.in_queue++;
|
||||
if (!s->io_q.blocked) {
|
||||
- if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) {
|
||||
+ if (s->io_q.in_queue >= laio_max_batch(s, laiocb->dev_max_batch)) {
|
||||
ioq_submit(s);
|
||||
} else {
|
||||
defer_call(laio_deferred_fn, s);
|
||||
@@ -425,14 +431,18 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
|
||||
AioContext *ctx = qemu_get_current_aio_context();
|
||||
struct qemu_laiocb laiocb = {
|
||||
.co = qemu_coroutine_self(),
|
||||
+ .offset = offset,
|
||||
.nbytes = qiov ? qiov->size : 0,
|
||||
.ctx = aio_get_linux_aio(ctx),
|
||||
.ret = -EINPROGRESS,
|
||||
- .is_read = (type == QEMU_AIO_READ),
|
||||
.qiov = qiov,
|
||||
+ .fd = fd,
|
||||
+ .type = type,
|
||||
+ .flags = flags,
|
||||
+ .dev_max_batch = dev_max_batch,
|
||||
};
|
||||
|
||||
- ret = laio_do_submit(fd, &laiocb, offset, type, flags, dev_max_batch);
|
||||
+ ret = laio_do_submit(&laiocb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
--
|
||||
2.47.3
|
||||
|
||||
215
kvm-linux-aio-Resubmit-tails-of-short-reads-writes.patch
Normal file
215
kvm-linux-aio-Resubmit-tails-of-short-reads-writes.patch
Normal file
@ -0,0 +1,215 @@
|
||||
From ad3a6c9b3487226d9622120eaea8218bd6050a04 Mon Sep 17 00:00:00 2001
|
||||
From: Hanna Czenczek <hreitz@redhat.com>
|
||||
Date: Tue, 24 Mar 2026 09:43:35 +0100
|
||||
Subject: [PATCH 2/4] linux-aio: Resubmit tails of short reads/writes
|
||||
|
||||
RH-Author: Hanna Czenczek <hreitz@redhat.com>
|
||||
RH-MergeRequest: 479: linux-aio/io-uring: Resubmit tails of short requests
|
||||
RH-Jira: RHEL-158224
|
||||
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
RH-Commit: [2/4] f97271e609a150acc04a15ffc85c40e7bcb00060 (hreitz/qemu-kvm-c-9-s)
|
||||
|
||||
Short reads/writes can happen. One way to reproduce them is via our
|
||||
FUSE export, with the following diff applied (%s/escaped // to apply --
|
||||
if you put plain diffs in commit messages, git-am will apply them, and I
|
||||
would rather avoid breaking FUSE accidentally via this patch):
|
||||
|
||||
escaped diff --git a/block/export/fuse.c b/block/export/fuse.c
|
||||
escaped index a2a478d293..67dc50a412 100644
|
||||
escaped --- a/block/export/fuse.c
|
||||
escaped +++ b/block/export/fuse.c
|
||||
@@ -828,7 +828,7 @@ static ssize_t coroutine_fn GRAPH_RDLOCK
|
||||
fuse_co_init(FuseExport *exp, struct fuse_init_out *out,
|
||||
const struct fuse_init_in_compat *in)
|
||||
{
|
||||
- const uint32_t supported_flags = FUSE_ASYNC_READ | FUSE_ASYNC_DIO;
|
||||
+ const uint32_t supported_flags = FUSE_ASYNC_READ;
|
||||
|
||||
if (in->major != 7) {
|
||||
error_report("FUSE major version mismatch: We have 7, but kernel has %"
|
||||
@@ -1060,6 +1060,8 @@ fuse_co_read(FuseExport *exp, void **bufptr, uint64_t offset, uint32_t size)
|
||||
void *buf;
|
||||
int ret;
|
||||
|
||||
+ size = MIN(size, 4096);
|
||||
+
|
||||
/* Limited by max_read, should not happen */
|
||||
if (size > FUSE_MAX_READ_BYTES) {
|
||||
return -EINVAL;
|
||||
@@ -1110,6 +1112,8 @@ fuse_co_write(FuseExport *exp, struct fuse_write_out *out,
|
||||
int64_t blk_len;
|
||||
int ret;
|
||||
|
||||
+ size = MIN(size, 4096);
|
||||
+
|
||||
QEMU_BUILD_BUG_ON(FUSE_MAX_WRITE_BYTES > BDRV_REQUEST_MAX_BYTES);
|
||||
/* Limited by max_write, should not happen */
|
||||
if (size > FUSE_MAX_WRITE_BYTES) {
|
||||
|
||||
Then:
|
||||
$ ./qemu-img create -f raw test.raw 8k
|
||||
Formatting 'test.raw', fmt=raw size=8192
|
||||
$ ./qemu-io -f raw -c 'write -P 42 0 8k' test.raw
|
||||
wrote 8192/8192 bytes at offset 0
|
||||
8 KiB, 1 ops; 00.00 sec (64.804 MiB/sec and 8294.9003 ops/sec)
|
||||
$ hexdump -C test.raw
|
||||
00000000 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |****************|
|
||||
*
|
||||
00002000
|
||||
|
||||
With aio=threads, short I/O works:
|
||||
$ storage-daemon/qemu-storage-daemon \
|
||||
--blockdev file,node-name=test,filename=test.raw \
|
||||
--export fuse,id=exp,node-name=test,mountpoint=test.raw,writable=true
|
||||
|
||||
Other shell:
|
||||
$ ./qemu-io --image-opts -c 'read -P 42 0 8k' \
|
||||
driver=file,filename=test.raw,cache.direct=on,aio=threads
|
||||
read 8192/8192 bytes at offset 0
|
||||
8 KiB, 1 ops; 00.00 sec (36.563 MiB/sec and 4680.0923 ops/sec)
|
||||
$ ./qemu-io --image-opts -c 'write -P 23 0 8k' \
|
||||
driver=file,filename=test.raw,cache.direct=on,aio=threads
|
||||
wrote 8192/8192 bytes at offset 0
|
||||
8 KiB, 1 ops; 00.00 sec (35.995 MiB/sec and 4607.2970 ops/sec)
|
||||
$ hexdump -C test.raw
|
||||
00000000 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 |................|
|
||||
*
|
||||
00002000
|
||||
|
||||
But with aio=native, it does not:
|
||||
$ ./qemu-io --image-opts -c 'read -P 23 0 8k' \
|
||||
driver=file,filename=test.raw,cache.direct=on,aio=native
|
||||
Pattern verification failed at offset 0, 8192 bytes
|
||||
read 8192/8192 bytes at offset 0
|
||||
8 KiB, 1 ops; 00.00 sec (86.155 MiB/sec and 11027.7900 ops/sec)
|
||||
$ ./qemu-io --image-opts -c 'write -P 42 0 8k' \
|
||||
driver=file,filename=test.raw,cache.direct=on,aio=native
|
||||
write failed: No space left on device
|
||||
$ hexdump -C test.raw
|
||||
00000000 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |****************|
|
||||
*
|
||||
00001000 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 |................|
|
||||
*
|
||||
00002000
|
||||
|
||||
This patch fixes that.
|
||||
|
||||
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
Message-ID: <20260324084338.37453-3-hreitz@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
(cherry picked from commit 7eca3d4883be8d328377001a9ea7ae9882b00f3c)
|
||||
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
---
|
||||
block/linux-aio.c | 56 ++++++++++++++++++++++++++++++++++++++++++-----
|
||||
1 file changed, 50 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/block/linux-aio.c b/block/linux-aio.c
|
||||
index c2c5e11946..84397de54c 100644
|
||||
--- a/block/linux-aio.c
|
||||
+++ b/block/linux-aio.c
|
||||
@@ -45,6 +45,10 @@ struct qemu_laiocb {
|
||||
size_t nbytes;
|
||||
QEMUIOVector *qiov;
|
||||
|
||||
+ /* For handling short reads/writes */
|
||||
+ size_t total_done;
|
||||
+ QEMUIOVector resubmit_qiov;
|
||||
+
|
||||
int fd;
|
||||
int type;
|
||||
BdrvRequestFlags flags;
|
||||
@@ -74,28 +78,61 @@ struct LinuxAioState {
|
||||
};
|
||||
|
||||
static void ioq_submit(LinuxAioState *s);
|
||||
+static int laio_do_submit(struct qemu_laiocb *laiocb);
|
||||
|
||||
static inline ssize_t io_event_ret(struct io_event *ev)
|
||||
{
|
||||
return (ssize_t)(((uint64_t)ev->res2 << 32) | ev->res);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Retry tail of short requests.
|
||||
+ */
|
||||
+static int laio_resubmit_short_io(struct qemu_laiocb *laiocb, size_t done)
|
||||
+{
|
||||
+ QEMUIOVector *resubmit_qiov = &laiocb->resubmit_qiov;
|
||||
+
|
||||
+ laiocb->total_done += done;
|
||||
+
|
||||
+ if (!resubmit_qiov->iov) {
|
||||
+ qemu_iovec_init(resubmit_qiov, laiocb->qiov->niov);
|
||||
+ } else {
|
||||
+ qemu_iovec_reset(resubmit_qiov);
|
||||
+ }
|
||||
+ qemu_iovec_concat(resubmit_qiov, laiocb->qiov,
|
||||
+ laiocb->total_done, laiocb->nbytes - laiocb->total_done);
|
||||
+
|
||||
+ return laio_do_submit(laiocb);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Completes an AIO request.
|
||||
*/
|
||||
static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
|
||||
{
|
||||
- int ret;
|
||||
+ ssize_t ret;
|
||||
|
||||
ret = laiocb->ret;
|
||||
if (ret != -ECANCELED) {
|
||||
- if (ret == laiocb->nbytes) {
|
||||
+ if (ret == laiocb->nbytes - laiocb->total_done) {
|
||||
ret = 0;
|
||||
+ } else if (ret > 0 && (laiocb->type == QEMU_AIO_READ ||
|
||||
+ laiocb->type == QEMU_AIO_WRITE)) {
|
||||
+ ret = laio_resubmit_short_io(laiocb, ret);
|
||||
+ if (!ret) {
|
||||
+ return;
|
||||
+ }
|
||||
} else if (ret >= 0) {
|
||||
- /* Short reads mean EOF, pad with zeros. */
|
||||
+ /*
|
||||
+ * For normal reads and writes, we only get here if ret == 0, which
|
||||
+ * means EOF for reads and ENOSPC for writes.
|
||||
+ * For zone-append, we get here with any ret >= 0, which we just
|
||||
+ * treat as ENOSPC, too (safer than resubmitting, probably, but not
|
||||
+ * 100 % clear).
|
||||
+ */
|
||||
if (laiocb->type == QEMU_AIO_READ) {
|
||||
- qemu_iovec_memset(laiocb->qiov, ret, 0,
|
||||
- laiocb->qiov->size - ret);
|
||||
+ qemu_iovec_memset(laiocb->qiov, laiocb->total_done, 0,
|
||||
+ laiocb->qiov->size - laiocb->total_done);
|
||||
} else {
|
||||
ret = -ENOSPC;
|
||||
}
|
||||
@@ -103,6 +140,9 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
|
||||
}
|
||||
|
||||
laiocb->ret = ret;
|
||||
+ if (laiocb->resubmit_qiov.iov) {
|
||||
+ qemu_iovec_destroy(&laiocb->resubmit_qiov);
|
||||
+ }
|
||||
|
||||
/*
|
||||
* If the coroutine is already entered it must be in ioq_submit() and
|
||||
@@ -379,7 +419,11 @@ static int laio_do_submit(struct qemu_laiocb *laiocb)
|
||||
struct iocb *iocbs = &laiocb->iocb;
|
||||
QEMUIOVector *qiov = laiocb->qiov;
|
||||
int fd = laiocb->fd;
|
||||
- off_t offset = laiocb->offset;
|
||||
+ off_t offset = laiocb->offset + laiocb->total_done;
|
||||
+
|
||||
+ if (laiocb->resubmit_qiov.iov) {
|
||||
+ qiov = &laiocb->resubmit_qiov;
|
||||
+ }
|
||||
|
||||
switch (laiocb->type) {
|
||||
case QEMU_AIO_WRITE:
|
||||
--
|
||||
2.47.3
|
||||
|
||||
@ -158,7 +158,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \
|
||||
Summary: QEMU is a machine emulator and virtualizer
|
||||
Name: qemu-kvm
|
||||
Version: 10.1.0
|
||||
Release: 15%{?rcrel}%{?dist}%{?cc_suffix}.alma.1
|
||||
Release: 16%{?rcrel}%{?dist}%{?cc_suffix}.alma.1
|
||||
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
|
||||
# Epoch 15 used for RHEL 8
|
||||
# Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5)
|
||||
@ -429,6 +429,14 @@ Patch128: kvm-hw-uefi-add-variable-digest-to-vmstate.patch
|
||||
Patch129: kvm-block-Never-drop-BLOCK_IO_ERROR-with-action-stop-for.patch
|
||||
# For RHEL-155601 - Mirror job can miss writes during startup, corrupting the copy [rhel-10.2]
|
||||
Patch130: kvm-mirror-Fix-missed-dirty-bitmap-writes-during-startup.patch
|
||||
# For RHEL-158224 - qemu-kvm: disk writes of fewer bytes than requested is a retry condition, not necessarily an indication of ENOSPC [rhel-10.2]
|
||||
Patch131: kvm-linux-aio-Put-all-parameters-into-qemu_laiocb.patch
|
||||
# For RHEL-158224 - qemu-kvm: disk writes of fewer bytes than requested is a retry condition, not necessarily an indication of ENOSPC [rhel-10.2]
|
||||
Patch132: kvm-linux-aio-Resubmit-tails-of-short-reads-writes.patch
|
||||
# For RHEL-158224 - qemu-kvm: disk writes of fewer bytes than requested is a retry condition, not necessarily an indication of ENOSPC [rhel-10.2]
|
||||
Patch133: kvm-block-io_uring-avoid-potentially-getting-stuck-after.patch
|
||||
# For RHEL-158224 - qemu-kvm: disk writes of fewer bytes than requested is a retry condition, not necessarily an indication of ENOSPC [rhel-10.2]
|
||||
Patch134: kvm-io-uring-Resubmit-tails-of-short-writes.patch
|
||||
|
||||
# AlmaLinux Patch
|
||||
Patch2001: 2001-Add-ppc64-support.patch
|
||||
@ -1557,12 +1565,20 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Mar 27 2026 Eduard Abdullin <eabdullin@almalinux.org> - 18:10.1.0-15.alma.1
|
||||
* Tue Mar 31 2026 Eduard Abdullin <eabdullin@almalinux.org> - 18:10.1.0-16.alma.1
|
||||
- Enable QXL device build
|
||||
- Enable building for ppc64le
|
||||
- Re-added Spice support
|
||||
- Don't remove slof.bin for ppc64le
|
||||
|
||||
* Mon Mar 30 2026 Miroslav Rezanina <mrezanin@redhat.com> - 10.1.0-16
|
||||
- kvm-linux-aio-Put-all-parameters-into-qemu_laiocb.patch [RHEL-158224]
|
||||
- kvm-linux-aio-Resubmit-tails-of-short-reads-writes.patch [RHEL-158224]
|
||||
- kvm-block-io_uring-avoid-potentially-getting-stuck-after.patch [RHEL-158224]
|
||||
- kvm-io-uring-Resubmit-tails-of-short-writes.patch [RHEL-158224]
|
||||
- Resolves: RHEL-158224
|
||||
(qemu-kvm: disk writes of fewer bytes than requested is a retry condition, not necessarily an indication of ENOSPC [rhel-10.2])
|
||||
|
||||
* Thu Mar 26 2026 Miroslav Rezanina <mrezanin@redhat.com> - 10.1.0-15
|
||||
- kvm-mirror-Fix-missed-dirty-bitmap-writes-during-startup.patch [RHEL-155601]
|
||||
- Resolves: RHEL-155601
|
||||
|
||||
Loading…
Reference in New Issue
Block a user