Enable QXL device build

Enable building for ppc64le

Re-added Spice support

Don't remove slof.bin for ppc64le
This commit is contained in:
Eduard Abdullin 2026-08-24 16:41:10 +00:00 committed by root
parent 35ae37c0e7
commit 81048c7967
12 changed files with 1471 additions and 2 deletions

View File

@ -0,0 +1,121 @@
From b37120279fbb5615558dce392365527aae8169ea Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Tue, 21 Apr 2026 18:11:26 +0200
Subject: [PATCH 1/8] blkdebug: Add 'delay-ns' option
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 562: ide: Fix deadlock between TRIM and drain [10.2.z]
RH-Jira: RHEL-190702
RH-Acked-by: German Maglione <None>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/8] c07ed8a9897c2f8cd8efa5049ed100a1e42b9123 (kmwolf/rhel-qemu-kvm)
Sometimes reproducing a problem for debugging involves slow I/O, so
let's add something to blkdebug to make I/O slow when we need it. This
can be used either together with an error so that the request fails
after the delay, or with errno=0, which allows the request to succeed
after the delay.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20260421161132.99878-2-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit d5e4090177ad382e01084a1594a1a60a69f4c1cd)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/blkdebug.c | 15 ++++++++++++++-
qapi/block-core.json | 4 ++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/block/blkdebug.c b/block/blkdebug.c
index c54aee0c84..8954fc2977 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -95,6 +95,7 @@ typedef struct BlkdebugRule {
int immediately;
int once;
int64_t offset;
+ int64_t delay_ns;
} inject;
struct {
int new_state;
@@ -144,6 +145,10 @@ static QemuOptsList inject_error_opts = {
.name = "immediately",
.type = QEMU_OPT_BOOL,
},
+ {
+ .name = "delay-ns",
+ .type = QEMU_OPT_NUMBER,
+ },
{ /* end of list */ }
},
};
@@ -216,6 +221,8 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0);
rule->options.inject.immediately =
qemu_opt_get_bool(opts, "immediately", 0);
+ rule->options.inject.delay_ns =
+ qemu_opt_get_number(opts, "delay-ns", 0);
sector = qemu_opt_get_number(opts, "sector", -1);
rule->options.inject.offset =
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
@@ -594,6 +601,7 @@ static int coroutine_fn rule_check(BlockDriverState *bs, uint64_t offset,
BlkdebugRule *rule = NULL;
int error;
bool immediately;
+ int64_t delay_ns;
qemu_mutex_lock(&s->lock);
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
@@ -608,13 +616,14 @@ static int coroutine_fn rule_check(BlockDriverState *bs, uint64_t offset,
}
}
- if (!rule || !rule->options.inject.error) {
+ if (!rule) {
qemu_mutex_unlock(&s->lock);
return 0;
}
immediately = rule->options.inject.immediately;
error = rule->options.inject.error;
+ delay_ns = rule->options.inject.delay_ns;
if (rule->options.inject.once) {
QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
@@ -622,6 +631,10 @@ static int coroutine_fn rule_check(BlockDriverState *bs, uint64_t offset,
}
qemu_mutex_unlock(&s->lock);
+
+ if (delay_ns) {
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, delay_ns);
+ }
if (!immediately) {
aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
qemu_coroutine_yield();
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 0236936139..6cf8c1b9c8 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3909,6 +3909,9 @@
#
# @errno: error identifier (errno) to be returned; defaults to EIO
#
+# @delay-ns: request delay before completion in nanoseconds
+# (default: 0, since: 11.1)
+#
# @sector: specifies the sector index which has to be affected in
# order to actually trigger the event; defaults to "any sector"
#
@@ -3924,6 +3927,7 @@
'*state': 'int',
'*iotype': 'BlkdebugIOType',
'*errno': 'int',
+ '*delay-ns': 'int',
'*sector': 'int',
'*once': 'bool',
'*immediately': 'bool' } }
--
2.52.0

View File

@ -0,0 +1,203 @@
From bd929a4dbcd20cb9f6314c867726b59e18254994 Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Tue, 21 Apr 2026 18:11:27 +0200
Subject: [PATCH 2/8] block: Add blk_co_start/end_request() and
BDRV_REQ_NO_QUEUE
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 562: ide: Fix deadlock between TRIM and drain [10.2.z]
RH-Jira: RHEL-190702
RH-Acked-by: German Maglione <None>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [2/8] 12df3f715235d07f3b45fa3ee56e5e8aa45e866c (kmwolf/rhel-qemu-kvm)
If a device uses blk_inc/dec_in_flight() in order to build macro
operations that involve multiple requests for the block layer and that
need to be completed as a unit before the BlockBackend can be considered
drained, it sets the stage for a deadlock: When a drain is requested,
the inner request at the BlockBackend level will be queued in
blk_wait_while_drained() and wait until the drained section ends, but at
the same time, drain_begin can only return if the whole macro operation
at the device level has completed.
Introduce a new interface to allow implementing the logic correctly:
Instead of queueing individual requests, blk_co_start_request() calls
blk_wait_while_drained() once at the beginning. The individual requests
must then set BDRV_REQ_NO_QUEUE to avoid being queued and running into
the deadlock; being wrapped in blk_co_start/end_request() makes sure
that drain_begin waits for them and they don't sneak in when the
BlockBackend is supposed to already be quiescent.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20260421161132.99878-3-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 34a67637767d3ed1ac813c44effe827bbfba5996)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/block-backend.c | 38 +++++++++++++++++++++++--------
include/block/block-common.h | 11 ++++++++-
include/system/block-backend-io.h | 2 ++
3 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/block/block-backend.c b/block/block-backend.c
index d6df369188..a0f4c841da 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -82,6 +82,7 @@ struct BlockBackend {
QemuMutex queued_requests_lock; /* protects queued_requests */
CoQueue queued_requests;
bool disable_request_queuing; /* atomic */
+ int start_request_count; /* atomic */
VMChangeStateEntry *vmsh;
bool force_allow_inactivate;
@@ -1306,10 +1307,16 @@ bool blk_in_drain(BlockBackend *blk)
}
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
-static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
+static void coroutine_fn blk_wait_while_drained(BlockBackend *blk,
+ BdrvRequestFlags flags)
{
assert(blk->in_flight > 0);
+ if (flags & BDRV_REQ_NO_QUEUE) {
+ assert(qatomic_read(&blk->start_request_count));
+ return;
+ }
+
if (qatomic_read(&blk->quiesce_counter) &&
!qatomic_read(&blk->disable_request_queuing)) {
/*
@@ -1335,7 +1342,7 @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes,
BlockDriverState *bs;
IO_CODE();
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, flags);
GRAPH_RDLOCK_GUARD();
/* Call blk_bs() only after waiting, the graph may have changed */
@@ -1410,7 +1417,7 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes,
BlockDriverState *bs;
IO_CODE();
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, flags);
GRAPH_RDLOCK_GUARD();
/* Call blk_bs() only after waiting, the graph may have changed */
@@ -1523,6 +1530,19 @@ void blk_dec_in_flight(BlockBackend *blk)
aio_wait_kick();
}
+void coroutine_fn blk_co_start_request(BlockBackend *blk)
+{
+ blk_inc_in_flight(blk);
+ blk_wait_while_drained(blk, 0);
+ qatomic_inc(&blk->start_request_count);
+}
+
+void blk_end_request(BlockBackend *blk)
+{
+ qatomic_dec(&blk->start_request_count);
+ blk_dec_in_flight(blk);
+}
+
static void error_callback_bh(void *opaque)
{
struct BlockBackendAIOCB *acb = opaque;
@@ -1741,7 +1761,7 @@ blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
{
IO_CODE();
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, 0);
GRAPH_RDLOCK_GUARD();
if (!blk_co_is_available(blk)) {
@@ -1788,7 +1808,7 @@ blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes)
int ret;
IO_CODE();
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, 0);
GRAPH_RDLOCK_GUARD();
ret = blk_check_byte_request(blk, offset, bytes);
@@ -1834,7 +1854,7 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset,
static int coroutine_fn blk_co_do_flush(BlockBackend *blk)
{
IO_CODE();
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, 0);
GRAPH_RDLOCK_GUARD();
if (!blk_co_is_available(blk)) {
@@ -2009,7 +2029,7 @@ int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset,
IO_CODE();
blk_inc_in_flight(blk); /* increase before waiting */
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, 0);
GRAPH_RDLOCK_GUARD();
if (!blk_is_available(blk)) {
blk_dec_in_flight(blk);
@@ -2034,7 +2054,7 @@ int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op,
IO_CODE();
blk_inc_in_flight(blk);
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, 0);
GRAPH_RDLOCK_GUARD();
ret = blk_check_byte_request(blk, offset, len);
@@ -2058,7 +2078,7 @@ int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset,
IO_CODE();
blk_inc_in_flight(blk);
- blk_wait_while_drained(blk);
+ blk_wait_while_drained(blk, flags);
GRAPH_RDLOCK_GUARD();
if (!blk_is_available(blk)) {
blk_dec_in_flight(blk);
diff --git a/include/block/block-common.h b/include/block/block-common.h
index c8c626daea..895ea17541 100644
--- a/include/block/block-common.h
+++ b/include/block/block-common.h
@@ -215,8 +215,17 @@ typedef enum {
*/
BDRV_REQ_NO_WAIT = 0x400,
+ /*
+ * Used between blk_co_start_request() and blk_end_request() to avoid
+ * that the request waits in a drained BlockBackend until the drained
+ * section ends. Waiting would cause a deadlock because drain waits for
+ * blk_end_request() to be called, but the request never completes
+ * because it waits for the drain to end.
+ */
+ BDRV_REQ_NO_QUEUE = 0x800,
+
/* Mask of valid flags */
- BDRV_REQ_MASK = 0x7ff,
+ BDRV_REQ_MASK = 0xfff,
} BdrvRequestFlags;
#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */
diff --git a/include/system/block-backend-io.h b/include/system/block-backend-io.h
index ba8dfcc7d0..59841e04a8 100644
--- a/include/system/block-backend-io.h
+++ b/include/system/block-backend-io.h
@@ -71,6 +71,8 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
void blk_inc_in_flight(BlockBackend *blk);
void blk_dec_in_flight(BlockBackend *blk);
+void coroutine_fn blk_co_start_request(BlockBackend *blk);
+void blk_end_request(BlockBackend *blk);
bool coroutine_fn GRAPH_RDLOCK blk_co_is_inserted(BlockBackend *blk);
bool co_wrapper_mixed_bdrv_rdlock blk_is_inserted(BlockBackend *blk);
--
2.52.0

View File

@ -0,0 +1,174 @@
From f834c37186b266b515c3f7e6b15a5e87e153f699 Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Tue, 21 Apr 2026 18:11:28 +0200
Subject: [PATCH 3/8] block: Add flags parameter to blk_*_pdiscard()
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 562: ide: Fix deadlock between TRIM and drain [10.2.z]
RH-Jira: RHEL-190702
RH-Acked-by: German Maglione <None>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [3/8] 6ce7ccfc577d41eda1283271dd5ee7913a1266c5 (kmwolf/rhel-qemu-kvm)
All existing callers pass 0, but we need a way to pass BDRV_REQ_NO_QUEUE
for discard requests.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20260421161132.99878-4-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 53074ba0330ae8831abbae2521c012e1d9072ed3)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/block-backend.c | 11 ++++++-----
block/export/virtio-blk-handler.c | 2 +-
block/mirror.c | 4 ++--
include/system/block-backend-io.h | 4 ++--
nbd/server.c | 2 +-
qemu-io-cmds.c | 2 +-
tests/unit/test-block-iothread.c | 4 ++--
7 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/block/block-backend.c b/block/block-backend.c
index a0f4c841da..d97b26b743 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1803,12 +1803,13 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
static int coroutine_fn
-blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes)
+blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes,
+ BdrvRequestFlags flags)
{
int ret;
IO_CODE();
- blk_wait_while_drained(blk, 0);
+ blk_wait_while_drained(blk, flags);
GRAPH_RDLOCK_GUARD();
ret = blk_check_byte_request(blk, offset, bytes);
@@ -1824,7 +1825,7 @@ static void coroutine_fn blk_aio_pdiscard_entry(void *opaque)
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
- rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, acb->bytes);
+ rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, acb->bytes, 0);
blk_aio_complete(acb);
}
@@ -1838,13 +1839,13 @@ BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk,
}
int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset,
- int64_t bytes)
+ int64_t bytes, BdrvRequestFlags flags)
{
int ret;
IO_OR_GS_CODE();
blk_inc_in_flight(blk);
- ret = blk_co_do_pdiscard(blk, offset, bytes);
+ ret = blk_co_do_pdiscard(blk, offset, bytes, flags);
blk_dec_in_flight(blk);
return ret;
diff --git a/block/export/virtio-blk-handler.c b/block/export/virtio-blk-handler.c
index bc1cec6757..b82baae553 100644
--- a/block/export/virtio-blk-handler.c
+++ b/block/export/virtio-blk-handler.c
@@ -121,7 +121,7 @@ virtio_blk_discard_write_zeroes(VirtioBlkHandler *handler, struct iovec *iov,
}
if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
- bytes) == 0) {
+ bytes, 0) == 0) {
return VIRTIO_BLK_S_OK;
}
}
diff --git a/block/mirror.c b/block/mirror.c
index f01be99b55..c87f1e205b 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -454,7 +454,7 @@ static void coroutine_fn mirror_co_discard(void *opaque)
*op->bytes_handled = op->bytes;
op->is_in_flight = true;
- ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes);
+ ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes, 0);
mirror_write_complete(op, ret);
}
@@ -1527,7 +1527,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
zero_bitmap_end - zero_bitmap_offset);
}
assert(!qiov);
- ret = blk_co_pdiscard(job->target, offset, bytes);
+ ret = blk_co_pdiscard(job->target, offset, bytes, 0);
break;
default:
diff --git a/include/system/block-backend-io.h b/include/system/block-backend-io.h
index 59841e04a8..91c48299b7 100644
--- a/include/system/block-backend-io.h
+++ b/include/system/block-backend-io.h
@@ -217,9 +217,9 @@ int co_wrapper_mixed blk_zone_append(BlockBackend *blk, int64_t *offset,
BdrvRequestFlags flags);
int co_wrapper_mixed blk_pdiscard(BlockBackend *blk, int64_t offset,
- int64_t bytes);
+ int64_t bytes, BdrvRequestFlags flags);
int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset,
- int64_t bytes);
+ int64_t bytes, BdrvRequestFlags flags);
int co_wrapper_mixed blk_flush(BlockBackend *blk);
int coroutine_fn blk_co_flush(BlockBackend *blk);
diff --git a/nbd/server.c b/nbd/server.c
index d242be9811..c72dee7a3b 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -2982,7 +2982,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
"flush failed", errp);
case NBD_CMD_TRIM:
- ret = blk_co_pdiscard(exp->common.blk, request->from, request->len);
+ ret = blk_co_pdiscard(exp->common.blk, request->from, request->len, 0);
if (ret >= 0 && request->flags & NBD_CMD_FLAG_FUA) {
ret = blk_co_flush(exp->common.blk);
}
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 13e0330162..f6d077908f 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -2201,7 +2201,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
}
clock_gettime(CLOCK_MONOTONIC, &t1);
- ret = blk_pdiscard(blk, offset, bytes);
+ ret = blk_pdiscard(blk, offset, bytes, 0);
clock_gettime(CLOCK_MONOTONIC, &t2);
if (ret < 0) {
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
index e26b3be593..5273ff235a 100644
--- a/tests/unit/test-block-iothread.c
+++ b/tests/unit/test-block-iothread.c
@@ -270,11 +270,11 @@ static void test_sync_op_blk_pdiscard(BlockBackend *blk)
int ret;
/* Early success: UNMAP not supported */
- ret = blk_pdiscard(blk, 0, 512);
+ ret = blk_pdiscard(blk, 0, 512, 0);
g_assert_cmpint(ret, ==, 0);
/* Early error: Negative offset */
- ret = blk_pdiscard(blk, -2, 512);
+ ret = blk_pdiscard(blk, -2, 512, 0);
g_assert_cmpint(ret, ==, -EIO);
}
--
2.52.0

View File

@ -0,0 +1,103 @@
From c92790705ea64e29191ad00bd61e1ad13011c6b4 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Tue, 7 Oct 2025 10:16:59 -0400
Subject: [PATCH 2/3] block: use pwrite_zeroes_alignment when writing first
sector
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 565: block: Fix image creation on storage with 4k sector size
RH-Jira: RHEL-207389
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Acked-by: German Maglione <None>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Commit: [2/3] 1854afab367accbe5a4ffdcbb7cea180595b6bdc (kmwolf/rhel-qemu-kvm)
Since commit 5634622bcb33 ("file-posix: allow BLKZEROOUT with -t
writeback"), qemu-img create errors out on a Linux loop block device
with a 4 KB sector size:
# dd if=/dev/zero of=blockfile bs=1M count=1024
# losetup --sector-size 4096 /dev/loop0 blockfile
# qemu-img create -f raw /dev/loop0 1G
Formatting '/dev/loop0', fmt=raw size=1073741824
qemu-img: /dev/loop0: Failed to clear the new image's first sector: Invalid argument
Use the pwrite_zeroes_alignment block limit to avoid misaligned
fallocate(2) or ioctl(BLKZEROOUT) in the block/file-posix.c block
driver.
Cc: qemu-stable@nongnu.org
Fixes: 5634622bcb33 ("file-posix: allow BLKZEROOUT with -t writeback")
Reported-by: Jean-Louis Dupond <jean-louis@dupond.be>
Buglink: https://gitlab.com/qemu-project/qemu/-/issues/3127
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-ID: <20251007141700.71891-3-stefanha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit d704a13d2c025779bc91d04e127427347ddcf3b3)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block.c | 3 ++-
block/block-backend.c | 11 +++++++++++
include/system/block-backend-io.h | 1 +
3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/block.c b/block.c
index 8848e9a7ed..be77e03904 100644
--- a/block.c
+++ b/block.c
@@ -606,12 +606,13 @@ create_file_fallback_zero_first_sector(BlockBackend *blk,
int64_t current_size,
Error **errp)
{
+ uint32_t alignment = blk_get_pwrite_zeroes_alignment(blk);
int64_t bytes_to_clear;
int ret;
GLOBAL_STATE_CODE();
- bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE);
+ bytes_to_clear = MIN(current_size, MAX(BDRV_SECTOR_SIZE, alignment));
if (bytes_to_clear) {
ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
if (ret < 0) {
diff --git a/block/block-backend.c b/block/block-backend.c
index d97b26b743..850f2ecec2 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -2326,6 +2326,17 @@ uint32_t blk_get_request_alignment(BlockBackend *blk)
return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
}
+/* Returns the optimal write zeroes alignment, in bytes; guaranteed nonzero */
+uint32_t blk_get_pwrite_zeroes_alignment(BlockBackend *blk)
+{
+ BlockDriverState *bs = blk_bs(blk);
+ IO_CODE();
+ if (!bs) {
+ return BDRV_SECTOR_SIZE;
+ }
+ return bs->bl.pwrite_zeroes_alignment ?: bs->bl.request_alignment;
+}
+
/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */
uint64_t blk_get_max_hw_transfer(BlockBackend *blk)
{
diff --git a/include/system/block-backend-io.h b/include/system/block-backend-io.h
index 91c48299b7..fd84723d9d 100644
--- a/include/system/block-backend-io.h
+++ b/include/system/block-backend-io.h
@@ -118,6 +118,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
void *opaque, int ret);
uint32_t blk_get_request_alignment(BlockBackend *blk);
+uint32_t blk_get_pwrite_zeroes_alignment(BlockBackend *blk);
uint32_t blk_get_max_transfer(BlockBackend *blk);
uint64_t blk_get_max_hw_transfer(BlockBackend *blk);
--
2.52.0

View File

@ -0,0 +1,65 @@
From 9aa4988b7506ff56f7c02ac0794f29b0766adb6f Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Tue, 7 Oct 2025 10:16:58 -0400
Subject: [PATCH 1/3] file-posix: populate pwrite_zeroes_alignment
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 565: block: Fix image creation on storage with 4k sector size
RH-Jira: RHEL-207389
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Acked-by: German Maglione <None>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Commit: [1/3] dd57fecd1f1d62be600451c47efb061925e89154 (kmwolf/rhel-qemu-kvm)
Linux block devices require write zeroes alignment whereas files do not.
It may come as a surprise that block devices opened in buffered I/O mode
require the alignment for write zeroes requests although normal
read/write requests do not.
Therefore it is necessary to populate the pwrite_zeroes_alignment field.
Cc: qemu-stable@nongnu.org
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-ID: <20251007141700.71891-2-stefanha@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 98e788b91ad037193b1fb375561ef7e0fef3c2fd)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/file-posix.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/block/file-posix.c b/block/file-posix.c
index ffca37130b..0129413273 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1607,6 +1607,22 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
bs->bl.pdiscard_alignment = dalign;
}
+
+#ifdef __linux__
+ /*
+ * Linux requires logical block size alignment for write zeroes even
+ * when normal reads/writes do not require alignment.
+ */
+ if (!s->needs_alignment) {
+ ret = probe_logical_blocksize(s->fd,
+ &bs->bl.pwrite_zeroes_alignment);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Failed to probe logical block size");
+ return;
+ }
+ }
+#endif /* __linux__ */
}
raw_refresh_zoned_limits(bs, &st, errp);
--
2.52.0

View File

@ -0,0 +1,163 @@
From e08abed8efc20906c2aeabeadd36f4f3e3dd3142 Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Tue, 21 Apr 2026 18:11:30 +0200
Subject: [PATCH 5/8] ide: Clean up ide_trim_co_entry() to be idiomatic
coroutine code
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 562: ide: Fix deadlock between TRIM and drain [10.2.z]
RH-Jira: RHEL-190702
RH-Acked-by: German Maglione <None>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [5/8] 8ecbfb819ce278daf17904c328464a474ba9c4bc (kmwolf/rhel-qemu-kvm)
The previous commit did a minimal conversion of the callback based state
machine for TRIM to a coroutine in order to fix a bug. Refactor it to
actually look like normal coroutine based code, which improves its
readability.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20260421161132.99878-6-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit c1c71a7e167fdabaa9827d00c0be3aeafebdd921)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
hw/ide/core.c | 87 +++++++++++++++++++++++----------------------------
1 file changed, 39 insertions(+), 48 deletions(-)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 82c3ada14a..8b9f06547b 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -420,18 +420,15 @@ typedef struct TrimAIOCB {
QEMUBH *bh;
int ret;
QEMUIOVector *qiov;
- int i, j;
+ bool canceled;
} TrimAIOCB;
static void trim_aio_cancel(BlockAIOCB *acb)
{
TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common);
- /* Exit the loop so ide_issue_trim_cb will not continue */
- iocb->j = iocb->qiov->niov - 1;
- iocb->i = (iocb->qiov->iov[iocb->j].iov_len / 8) - 1;
-
- iocb->ret = -ECANCELED;
+ /* Exit the loop so ide_trim_co_entry will not continue */
+ iocb->canceled = true;
}
static const AIOCBInfo trim_aiocb_info = {
@@ -458,60 +455,55 @@ static void coroutine_fn ide_trim_co_entry(void *opaque)
{
TrimAIOCB *iocb = opaque;
IDEState *s = iocb->s;
- int ret = 0;
+ int i, j;
+ int ret;
/* Paired with blk_end_request in ide_trim_bh_cb() */
blk_co_start_request(s->blk);
-loop:
- if (iocb->i >= 0) {
- if (ret >= 0) {
- block_acct_done(blk_get_stats(s->blk), &s->acct);
- } else {
- block_acct_failed(blk_get_stats(s->blk), &s->acct);
- }
- }
+ for (j = 0; j < iocb->qiov->niov; j++) {
+ for (i = 0; i < iocb->qiov->iov[j].iov_len / 8; i++) {
+ uint64_t *buffer = iocb->qiov->iov[j].iov_base;
- if (ret >= 0) {
- while (iocb->j < iocb->qiov->niov) {
- int j = iocb->j;
- while (++iocb->i < iocb->qiov->iov[j].iov_len / 8) {
- int i = iocb->i;
- uint64_t *buffer = iocb->qiov->iov[j].iov_base;
+ /* 6-byte LBA + 2-byte range per entry */
+ uint64_t entry = le64_to_cpu(buffer[i]);
+ uint64_t sector = entry & 0x0000ffffffffffffULL;
+ uint16_t count = entry >> 48;
- /* 6-byte LBA + 2-byte range per entry */
- uint64_t entry = le64_to_cpu(buffer[i]);
- uint64_t sector = entry & 0x0000ffffffffffffULL;
- uint16_t count = entry >> 48;
+ if (count == 0) {
+ continue;
+ }
- if (count == 0) {
- continue;
- }
+ if (iocb->canceled) {
+ iocb->ret = -ECANCELED;
+ goto done;
+ }
- if (!ide_sect_range_ok(s, sector, count)) {
- block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP);
- iocb->ret = -EINVAL;
- goto done;
- }
+ if (!ide_sect_range_ok(s, sector, count)) {
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_UNMAP);
+ iocb->ret = -EINVAL;
+ goto done;
+ }
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP);
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP);
- /* Got an entry! Submit and exit. */
- ret = blk_co_pdiscard(s->blk,
- sector << BDRV_SECTOR_BITS,
- count << BDRV_SECTOR_BITS,
- BDRV_REQ_NO_QUEUE);
- goto loop;
+ /* Got an entry! Submit and exit. */
+ ret = blk_co_pdiscard(s->blk,
+ sector << BDRV_SECTOR_BITS,
+ count << BDRV_SECTOR_BITS,
+ BDRV_REQ_NO_QUEUE);
+ if (ret >= 0) {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ } else {
+ iocb->ret = ret;
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ goto done;
}
-
- iocb->j++;
- iocb->i = -1;
}
- } else {
- iocb->ret = ret;
}
+ iocb->ret = 0;
done:
if (iocb->bh) {
replay_bh_schedule_event(iocb->bh);
@@ -533,8 +525,7 @@ BlockAIOCB *ide_issue_trim(
&DEVICE(dev)->mem_reentrancy_guard);
iocb->ret = 0;
iocb->qiov = qiov;
- iocb->i = -1;
- iocb->j = 0;
+ iocb->canceled = false;
co = qemu_coroutine_create(ide_trim_co_entry, iocb);
aio_co_enter(qemu_get_current_aio_context(), co);
--
2.52.0

View File

@ -0,0 +1,141 @@
From 1af428a8371e7c95be02569a551095a93f17a4ac Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Tue, 21 Apr 2026 18:11:29 +0200
Subject: [PATCH 4/8] ide: Minimal fix for deadlock between TRIM and drain
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 562: ide: Fix deadlock between TRIM and drain [10.2.z]
RH-Jira: RHEL-190702
RH-Acked-by: German Maglione <None>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [4/8] 339ddf749cae0dace22058df1998a80fa570d4b4 (kmwolf/rhel-qemu-kvm)
The implementation of TRIM in IDE can chain multiple discard requests
and uses blk_inc/dec_in_flight() to make sure that the whole TRIM
operation has completed when the device needs to be quiescent (e.g. for
the drain when performing an IDE reset, it would be bad if an IDE
request like TRIM were still in flight).
The problem is that each drain request calls blk_wait_while_drained()
and when draining, it waits until the drained section ends. At the same
time, drain_begin can only return if the whole TRIM operation has
completed. This is a classic deadlock.
Use blk_co_start/end_request() and BDRV_REQ_NO_QUEUE to avoid the
problem. This requires moving the TRIM state machine to a coroutine.
This commit does the minimal conversion so that we do have a coroutine
that works for the fix, but it still looks much like a callback-based
implementation. This will be cleaned up in the next patch.
Cc: qemu-stable@nongnu.org
Fixes: 7e5cdb345f77 ('ide: Increment BB in-flight counter for TRIM BH')
Buglink: https://redhat.atlassian.net/browse/RHEL-121686
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20260421161132.99878-5-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 095c08a7ba68cabaa6e0ce7a8a0804a949542c4c)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
hw/ide/core.c | 37 ++++++++++++++++++-------------------
1 file changed, 18 insertions(+), 19 deletions(-)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index b14983ec54..82c3ada14a 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -420,7 +420,6 @@ typedef struct TrimAIOCB {
QEMUBH *bh;
int ret;
QEMUIOVector *qiov;
- BlockAIOCB *aiocb;
int i, j;
} TrimAIOCB;
@@ -433,11 +432,6 @@ static void trim_aio_cancel(BlockAIOCB *acb)
iocb->i = (iocb->qiov->iov[iocb->j].iov_len / 8) - 1;
iocb->ret = -ECANCELED;
-
- if (iocb->aiocb) {
- blk_aio_cancel_async(iocb->aiocb);
- iocb->aiocb = NULL;
- }
}
static const AIOCBInfo trim_aiocb_info = {
@@ -456,15 +450,20 @@ static void ide_trim_bh_cb(void *opaque)
iocb->bh = NULL;
qemu_aio_unref(iocb);
- /* Paired with an increment in ide_issue_trim() */
- blk_dec_in_flight(blk);
+ /* Paired with blk_co_start_request in ide_trim_co_entry() */
+ blk_end_request(blk);
}
-static void ide_issue_trim_cb(void *opaque, int ret)
+static void coroutine_fn ide_trim_co_entry(void *opaque)
{
TrimAIOCB *iocb = opaque;
IDEState *s = iocb->s;
+ int ret = 0;
+
+ /* Paired with blk_end_request in ide_trim_bh_cb() */
+ blk_co_start_request(s->blk);
+loop:
if (iocb->i >= 0) {
if (ret >= 0) {
block_acct_done(blk_get_stats(s->blk), &s->acct);
@@ -499,11 +498,11 @@ static void ide_issue_trim_cb(void *opaque, int ret)
count << BDRV_SECTOR_BITS, BLOCK_ACCT_UNMAP);
/* Got an entry! Submit and exit. */
- iocb->aiocb = blk_aio_pdiscard(s->blk,
- sector << BDRV_SECTOR_BITS,
- count << BDRV_SECTOR_BITS,
- ide_issue_trim_cb, opaque);
- return;
+ ret = blk_co_pdiscard(s->blk,
+ sector << BDRV_SECTOR_BITS,
+ count << BDRV_SECTOR_BITS,
+ BDRV_REQ_NO_QUEUE);
+ goto loop;
}
iocb->j++;
@@ -514,7 +513,6 @@ static void ide_issue_trim_cb(void *opaque, int ret)
}
done:
- iocb->aiocb = NULL;
if (iocb->bh) {
replay_bh_schedule_event(iocb->bh);
}
@@ -527,9 +525,7 @@ BlockAIOCB *ide_issue_trim(
IDEState *s = opaque;
IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master;
TrimAIOCB *iocb;
-
- /* Paired with a decrement in ide_trim_bh_cb() */
- blk_inc_in_flight(s->blk);
+ Coroutine *co;
iocb = blk_aio_get(&trim_aiocb_info, s->blk, cb, cb_opaque);
iocb->s = s;
@@ -539,7 +535,10 @@ BlockAIOCB *ide_issue_trim(
iocb->qiov = qiov;
iocb->i = -1;
iocb->j = 0;
- ide_issue_trim_cb(iocb, 0);
+
+ co = qemu_coroutine_create(ide_trim_co_entry, iocb);
+ aio_co_enter(qemu_get_current_aio_context(), co);
+
return &iocb->common;
}
--
2.52.0

View File

@ -0,0 +1,91 @@
From 16cf591d59298ccc7cc1fe55427f2e2066d852d1 Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Tue, 21 Apr 2026 18:11:31 +0200
Subject: [PATCH 6/8] ide-test: Factor out wait_dma_completion()
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 562: ide: Fix deadlock between TRIM and drain [10.2.z]
RH-Jira: RHEL-190702
RH-Acked-by: German Maglione <None>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [6/8] 52ad17b4233e8589241928f2cbc8def83a0863de (kmwolf/rhel-qemu-kvm)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20260421161132.99878-7-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 92854c9c7539bdbf4f9c1abb33dd3ba59ff91e58)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
tests/qtest/ide-test.c | 48 +++++++++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 19 deletions(-)
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index ceee444a9e..c6dcb2c074 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -200,6 +200,34 @@ static uint64_t trim_range_le(uint64_t sector, uint16_t count)
return cpu_to_le64(((uint64_t)count << 48) + sector);
}
+static uint8_t wait_dma_completion(QTestState *qts, QPCIDevice *dev,
+ QPCIBar bmdma_bar, QPCIBar ide_bar)
+{
+ uint8_t status;
+
+ /* Wait for the DMA transfer to complete */
+ do {
+ status = qpci_io_readb(dev, bmdma_bar, bmreg_status);
+ } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
+
+ g_assert_cmpint(qtest_get_irq(qts, IDE_PRIMARY_IRQ), ==,
+ !!(status & BM_STS_INTR));
+
+ /* Check IDE status code */
+ assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ);
+
+ /* Reading the status register clears the IRQ */
+ g_assert(!qtest_get_irq(qts, IDE_PRIMARY_IRQ));
+
+ /* Stop DMA transfer if still active */
+ if (status & BM_STS_ACTIVE) {
+ qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0);
+ }
+
+ return status;
+}
+
static int send_dma_request(QTestState *qts, int cmd, uint64_t sector,
int nb_sectors, PrdtEntry *prdt, int prdt_entries,
void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar,
@@ -280,25 +308,7 @@ static int send_dma_request(QTestState *qts, int cmd, uint64_t sector,
qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0);
}
- /* Wait for the DMA transfer to complete */
- do {
- status = qpci_io_readb(dev, bmdma_bar, bmreg_status);
- } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
-
- g_assert_cmpint(qtest_get_irq(qts, IDE_PRIMARY_IRQ), ==,
- !!(status & BM_STS_INTR));
-
- /* Check IDE status code */
- assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY);
- assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ);
-
- /* Reading the status register clears the IRQ */
- g_assert(!qtest_get_irq(qts, IDE_PRIMARY_IRQ));
-
- /* Stop DMA transfer if still active */
- if (status & BM_STS_ACTIVE) {
- qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0);
- }
+ status = wait_dma_completion(qts, dev, bmdma_bar, ide_bar);
free_pci_device(dev);
--
2.52.0

View File

@ -0,0 +1,181 @@
From ffd0b4cfc9ad6b40eecc982c971bab08d8a3df4d Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Tue, 21 Apr 2026 18:11:32 +0200
Subject: [PATCH 7/8] ide-test: Test reset during TRIM
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 562: ide: Fix deadlock between TRIM and drain [10.2.z]
RH-Jira: RHEL-190702
RH-Acked-by: German Maglione <None>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [7/8] e74433de82754973428769f19fb387acc34b0455 (kmwolf/rhel-qemu-kvm)
This is a regression test for the bug fixed in the previous commits, a
deadlock between the drain issued by an IDE reset and the TRIM state
machine.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20260421161132.99878-8-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 2fa24e9755994f76f08ea2452215eb50f26f4c21)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
tests/qtest/ide-test.c | 95 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 87 insertions(+), 8 deletions(-)
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index c6dcb2c074..721e78170b 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -41,8 +41,11 @@
#define IDE_PCI_FUNC 1
#define IDE_BASE 0x1f0
+#define IDE_BASE2 0x3f6
#define IDE_PRIMARY_IRQ 14
+#define IDE_CTRL_RESET 0x04
+
#define ATAPI_BLOCK_SIZE 2048
/* How many bytes to receive via ATAPI PIO at one time.
@@ -99,6 +102,7 @@ enum {
CMDF_ABORT = 0x100,
CMDF_NO_BM = 0x200,
+ CMDF_NO_WAIT = 0x400,
};
enum {
@@ -228,21 +232,21 @@ static uint8_t wait_dma_completion(QTestState *qts, QPCIDevice *dev,
return status;
}
-static int send_dma_request(QTestState *qts, int cmd, uint64_t sector,
- int nb_sectors, PrdtEntry *prdt, int prdt_entries,
- void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar,
- uint64_t sector, int nb_sectors))
+static int send_dma_request_dev(QTestState *qts, QPCIDevice *dev,
+ QPCIBar bmdma_bar, QPCIBar ide_bar, int cmd,
+ uint64_t sector, int nb_sectors,
+ PrdtEntry *prdt, int prdt_entries,
+ void(*post_exec)(QPCIDevice *dev,
+ QPCIBar ide_bar,
+ uint64_t sector,
+ int nb_sectors))
{
- QPCIDevice *dev;
- QPCIBar bmdma_bar, ide_bar;
uintptr_t guest_prdt;
size_t len;
bool from_dev;
uint8_t status;
int flags;
- dev = get_pci_device(qts, &bmdma_bar, &ide_bar);
-
flags = cmd & ~0xff;
cmd &= 0xff;
@@ -308,8 +312,28 @@ static int send_dma_request(QTestState *qts, int cmd, uint64_t sector,
qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0);
}
+ if (flags & CMDF_NO_WAIT) {
+ return 0;
+ }
+
status = wait_dma_completion(qts, dev, bmdma_bar, ide_bar);
+ return status;
+}
+
+static int send_dma_request(QTestState *qts, int cmd, uint64_t sector,
+ int nb_sectors, PrdtEntry *prdt, int prdt_entries,
+ void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar,
+ uint64_t sector, int nb_sectors))
+{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
+ uint8_t status;
+
+ dev = get_pci_device(qts, &bmdma_bar, &ide_bar);
+ status = send_dma_request_dev(qts, dev, bmdma_bar, ide_bar,
+ cmd, sector, nb_sectors, prdt, prdt_entries,
+ post_exec);
free_pci_device(dev);
return status;
@@ -457,6 +481,60 @@ static void test_bmdma_trim(void)
test_bmdma_teardown(qts);
}
+static void test_bmdma_trim_reset(void)
+{
+ QTestState *qts;
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar, ide_bar2;
+ uint8_t status;
+ const uint64_t trim_range[] = {
+ trim_range_le(0, 2),
+ trim_range_le(6, 8),
+ };
+ size_t len = 512;
+ uint8_t *buf;
+ uintptr_t guest_buf;
+ PrdtEntry prdt[1];
+
+ qts = ide_test_start(
+ "-blockdev file,filename=%s,node-name=img "
+ "-blockdev blkdebug,image=img,node-name=dbg,discard=unmap,"
+ "inject-error.0.event=none,inject-error.0.iotype=discard,"
+ "inject-error.0.errno=0,inject-error.0.delay-ns=1000000 "
+ "-device ide-hd,drive=dbg,bus=ide.0",
+ tmp_path[0]);
+ qtest_irq_intercept_in(qts, "ioapic");
+
+ guest_buf = guest_alloc(&guest_malloc, len);
+ prdt[0].addr = cpu_to_le32(guest_buf),
+ prdt[0].size = cpu_to_le32(len | PRDT_EOT),
+
+ dev = get_pci_device(qts, &bmdma_bar, &ide_bar);
+ ide_bar2 = qpci_legacy_iomap(dev, IDE_BASE2);
+
+ buf = g_malloc(len);
+
+ /* TRIM request with two segments */
+ *((uint64_t *)buf) = trim_range[0];
+ *((uint64_t *)buf + 1) = trim_range[1];
+
+ qtest_memwrite(qts, guest_buf, buf, 2 * sizeof(uint64_t));
+
+ send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_DSM | CMDF_NO_WAIT, 0, 1, prdt,
+ ARRAY_SIZE(prdt), NULL);
+
+ /* Reset the device while the first segment is in flight */
+ qpci_io_writeb(dev, ide_bar2, 0, IDE_CTRL_RESET);
+
+ status = wait_dma_completion(qts, dev, bmdma_bar, ide_bar);
+ g_assert_cmphex(status, ==, BM_STS_INTR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
+
+ free_pci_device(dev);
+ g_free(buf);
+ test_bmdma_teardown(qts);
+}
+
/*
* This test is developed according to the Programming Interface for
* Bus Master IDE Controller (Revision 1.0 5/16/94)
@@ -1138,6 +1216,7 @@ int main(int argc, char **argv)
qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
qtest_add_func("/ide/bmdma/trim", test_bmdma_trim);
+ qtest_add_func("/ide/bmdma/trim_reset", test_bmdma_trim_reset);
qtest_add_func("/ide/bmdma/various_prdts", test_bmdma_various_prdts);
qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster);
--
2.52.0

View File

@ -0,0 +1,116 @@
From f55e897a36ddca7116d08445a9237b6d34ceacd6 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Tue, 7 Oct 2025 10:17:00 -0400
Subject: [PATCH 3/3] iotests: add Linux loop device image creation test
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 565: block: Fix image creation on storage with 4k sector size
RH-Jira: RHEL-207389
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Acked-by: German Maglione <None>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Commit: [3/3] b878b225ca4cbe5d1ef012122472d58bda27ffff (kmwolf/rhel-qemu-kvm)
This qemu-iotests test case is based on the reproducer that Jean-Louis
Dupond <jean-louis@dupond.be> shared in
https://gitlab.com/qemu-project/qemu/-/issues/3127.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-ID: <20251007141700.71891-4-stefanha@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Tested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 59a1cf0cd31597d2f6e2c18dc400a1de8427d47d)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
tests/qemu-iotests/tests/loop-create-file | 59 +++++++++++++++++++
tests/qemu-iotests/tests/loop-create-file.out | 8 +++
2 files changed, 67 insertions(+)
create mode 100755 tests/qemu-iotests/tests/loop-create-file
create mode 100644 tests/qemu-iotests/tests/loop-create-file.out
diff --git a/tests/qemu-iotests/tests/loop-create-file b/tests/qemu-iotests/tests/loop-create-file
new file mode 100755
index 0000000000..5ec75b046b
--- /dev/null
+++ b/tests/qemu-iotests/tests/loop-create-file
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+# group: quick
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright Red Hat, Inc.
+#
+# Test Linux loop device image creation
+#
+# This test verifies #3127 "qemu-img create fails on loop device with sector size 4096"
+# https://gitlab.com/qemu-project/qemu/-/issues/3127
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+_cleanup() {
+ if [ -n "$loopdev" ]; then
+ sudo losetup --detach "$loopdev"
+ fi
+
+ _cleanup_test_img
+}
+
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+cd ..
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto file
+_supported_os Linux
+
+if ! sudo -n losetup &>/dev/null; then
+ _notrun "sudo losetup not available"
+fi
+
+echo
+echo "=== Create image on a 4 KB sector size loop device ==="
+echo
+
+_make_test_img -f $IMGFMT 1M
+
+loopdev=$(sudo losetup --sector-size 4096 --find --show "$TEST_IMG")
+if [ -z "$loopdev" ]; then
+ _fail
+fi
+
+sudo $QEMU_IMG_PROG create -f raw "$loopdev" 1M | \
+ sed -e "s#/dev/loop[0-9]\\+#LOOPDEV#g"
+
+# success, all done
+echo
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/tests/loop-create-file.out b/tests/qemu-iotests/tests/loop-create-file.out
new file mode 100644
index 0000000000..32d4155695
--- /dev/null
+++ b/tests/qemu-iotests/tests/loop-create-file.out
@@ -0,0 +1,8 @@
+QA output created by loop-create-file
+
+=== Create image on a 4 KB sector size loop device ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+Formatting 'LOOPDEV', fmt=raw size=1048576
+
+*** done
--
2.52.0

View File

@ -0,0 +1,61 @@
From fb2752faded838bfea82450db6a9a56510ea998f Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Tue, 26 May 2026 11:49:57 -0400
Subject: [PATCH] virtio-blk: add missing VIRTIO_BLK_T_SCSI_CMD size check
(CVE-2026-48914)
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
RH-MergeRequest: 560: virtio-blk: add missing VIRTIO_BLK_T_SCSI_CMD size check (CVE-2026-48914)
RH-Jira: RHEL-184529
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] 2c669222671cda70b6fbf30b813e2e927cb8399d
Check that the iovec containing struct virtio_scsi_inhdr is large enough
before storing an error value there.
Feifan Qian <bea1e@proton.me> pointed out that this can be used to
corrupt heap memory when the descriptor uses an MMIO address and a
length of 1, forcing QEMU to allocate a 1-byte heap bounce buffer.
virtio_stl_p() stores 4 bytes and therefore corrupts whatever is beyond
the bounce buffer.
Fixes: CVE-2026-48914
Fixes: f34e73cd69bd ("virtio-blk: report non-zero status when failing SG_IO requests")
Reported-by: Feifan Qian <bea1e@proton.me>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-ID: <20260526154957.1741622-1-stefanha@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit aeea0c2804c42f24915467a1e4c70e649e39b8e0)
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/block/virtio-blk.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 9bab2716c1..a0678300b4 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -200,10 +200,16 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
/*
* The scsi inhdr is placed in the second-to-last input segment, just
- * before the regular inhdr.
+ * before the regular inhdr. VIRTIO implementations normally do not rely on
+ * the precise message framing, but legacy implementations did and so we do
+ * too for the legacy virtio-blk SCSI request type.
*
* Just put anything nonzero so that the ioctl fails in the guest.
*/
+ if (elem->in_sg[elem->in_num - 2].iov_len != sizeof(*scsi)) {
+ status = VIRTIO_BLK_S_IOERR;
+ goto fail;
+ }
scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base;
virtio_stl_p(vdev, &scsi->errors, 255);
status = VIRTIO_BLK_S_UNSUPP;
--
2.52.0

View File

@ -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: 16%{?rcrel}%{?dist}%{?cc_suffix}.2.alma.1
Release: 16%{?rcrel}%{?dist}%{?cc_suffix}.5.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)
@ -449,6 +449,28 @@ Patch138: kvm-scsi-register-again-after-PREEMPT-without-reservatio.patch
Patch139: kvm-scsi-change-buf_size-to-unsigned-int-in-scsi_SG_IO.patch
# For RHEL-166033 - live migration failed or get failed WSFC test result during WSFC testing [rhel-10.2.z]
Patch140: kvm-scsi-handle-reservation-changes-across-migration.patch
# For RHEL-184529 - CVE-2026-48914 qemu-kvm: Heap buffer overflow in virtio-blk SCSI request handling [rhel-10.2.z]
Patch141: kvm-virtio-blk-add-missing-VIRTIO_BLK_T_SCSI_CMD-size-ch.patch
# For RHEL-190702 - qemu-kvm hung during drain after double pause [rhel-10.2.z]
Patch142: kvm-blkdebug-Add-delay-ns-option.patch
# For RHEL-190702 - qemu-kvm hung during drain after double pause [rhel-10.2.z]
Patch143: kvm-block-Add-blk_co_start-end_request-and-BDRV_REQ_NO_Q.patch
# For RHEL-190702 - qemu-kvm hung during drain after double pause [rhel-10.2.z]
Patch144: kvm-block-Add-flags-parameter-to-blk_-_pdiscard.patch
# For RHEL-190702 - qemu-kvm hung during drain after double pause [rhel-10.2.z]
Patch145: kvm-ide-Minimal-fix-for-deadlock-between-TRIM-and-drain.patch
# For RHEL-190702 - qemu-kvm hung during drain after double pause [rhel-10.2.z]
Patch146: kvm-ide-Clean-up-ide_trim_co_entry-to-be-idiomatic-corou.patch
# For RHEL-190702 - qemu-kvm hung during drain after double pause [rhel-10.2.z]
Patch147: kvm-ide-test-Factor-out-wait_dma_completion.patch
# For RHEL-190702 - qemu-kvm hung during drain after double pause [rhel-10.2.z]
Patch148: kvm-ide-test-Test-reset-during-TRIM.patch
# For RHEL-207389 - qemu-img create/convert fails on target block device with 4k sector size [rhel-10.2.z]
Patch149: kvm-file-posix-populate-pwrite_zeroes_alignment.patch
# For RHEL-207389 - qemu-img create/convert fails on target block device with 4k sector size [rhel-10.2.z]
Patch150: kvm-block-use-pwrite_zeroes_alignment-when-writing-first.patch
# For RHEL-207389 - qemu-img create/convert fails on target block device with 4k sector size [rhel-10.2.z]
Patch151: kvm-iotests-add-Linux-loop-device-image-creation-test.patch
# AlmaLinux Patch
Patch2001: 2001-Add-ppc64-support.patch
@ -1177,6 +1199,7 @@ install -D -p -m 0644 %{modprobe_kvm_conf} $RPM_BUILD_ROOT%{_sysconfdir}/modprob
mkdir -p %{buildroot}%{testsdir}/python
mkdir -p %{buildroot}%{testsdir}/tests
mkdir -p %{buildroot}%{testsdir}/tests/qemu-iotests
mkdir -p %{buildroot}%{testsdir}/tests/qtest
mkdir -p %{buildroot}%{testsdir}/scripts/qmp
@ -1195,6 +1218,9 @@ cp -ur %{qemu_kvm_build}/tests/qemu-iotests/* %{buildroot}%{testsdir}/tests/qemu
install -p -m 0644 %{_sourcedir}/README.tests %{buildroot}%{testsdir}/README
# Install qtests
find %{qemu_kvm_build}/tests/qtest/ -type f -executable -exec install -p -m 0755 {} %{buildroot}%{testsdir}/tests/qtest/ \;
# Do the actual qemu tree install
pushd %{qemu_kvm_build}
%make_install
@ -1577,12 +1603,36 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
%endif
%changelog
* Tue Jul 21 2026 Eduard Abdullin <eabdullin@almalinux.org> - 18:10.1.0-16.2.alma.1
* Mon Aug 24 2026 Eduard Abdullin <eabdullin@almalinux.org> - 18:10.1.0-16.5.alma.1
- Enable QXL device build
- Enable building for ppc64le
- Re-added Spice support
- Don't remove slof.bin for ppc64le
* Tue Aug 04 2026 Miroslav Rezanina <mrezanin@redhat.com> - 10.1.0-16.el10_2.5
- kvm-file-posix-populate-pwrite_zeroes_alignment.patch [RHEL-207389]
- kvm-block-use-pwrite_zeroes_alignment-when-writing-first.patch [RHEL-207389]
- kvm-iotests-add-Linux-loop-device-image-creation-test.patch [RHEL-207389]
- Resolves: RHEL-207389
(qemu-img create/convert fails on target block device with 4k sector size [rhel-10.2.z])
* Thu Jul 09 2026 Miroslav Rezanina <mrezanin@redhat.com> - 10.1.0-16.el10_2.4
- kvm-blkdebug-Add-delay-ns-option.patch [RHEL-190702]
- kvm-block-Add-blk_co_start-end_request-and-BDRV_REQ_NO_Q.patch [RHEL-190702]
- kvm-block-Add-flags-parameter-to-blk_-_pdiscard.patch [RHEL-190702]
- kvm-ide-Minimal-fix-for-deadlock-between-TRIM-and-drain.patch [RHEL-190702]
- kvm-ide-Clean-up-ide_trim_co_entry-to-be-idiomatic-corou.patch [RHEL-190702]
- kvm-ide-test-Factor-out-wait_dma_completion.patch [RHEL-190702]
- kvm-ide-test-Test-reset-during-TRIM.patch [RHEL-190702]
- kvm-spec-Install-qtests-into-qemu-kvm-tests-package.patch [RHEL-190702]
- Resolves: RHEL-190702
(qemu-kvm hung during drain after double pause [rhel-10.2.z])
* Fri Jun 19 2026 Miroslav Rezanina <mrezanin@redhat.com> - 10.1.0-16.el10_2.3
- kvm-virtio-blk-add-missing-VIRTIO_BLK_T_SCSI_CMD-size-ch.patch [RHEL-184529]
- Resolves: RHEL-184529
(CVE-2026-48914 qemu-kvm: Heap buffer overflow in virtio-blk SCSI request handling [rhel-10.2.z])
* Mon Jun 15 2026 Miroslav Rezanina <mrezanin@redhat.com> - 10.1.0-16.el10_2.2
- kvm-scsi-adjust-error_prepend-formatting.patch [RHEL-166007]
- kvm-scsi-always-send-valid-PREEMPT-TYPE-field.patch [RHEL-166007]