* [PULL v2 0/7] IDE patches
@ 2026-08-04 17:00 Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 1/7] tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test Denis V. Lunev
` (8 more replies)
0 siblings, 9 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
The following changes since commit e1705a25aff35635c360bbaba4c2731d019a422a:
Update version for v11.1.0-rc2 release (2026-07-29 06:16:08 -0400)
are available in the Git repository at:
https://gitlab.com/dlunev/qemu tags/pull-ide-2026-08-04
for you to fetch changes up to 9a249de6ae86124b436b5e839e285aeb333ea8c0:
tests/qtest/ahci: regression test for ATAPI read vs. drain (2026-08-04 18:40:34 +0200)
----------------------------------------------------------------
IDE patches
- fix a main-loop deadlock when an ATAPI PIO read spanning several
sectors is in flight while a drain starts: the nested sector fetch
was issued synchronously from inside the completion of the first
read and queued behind the drain, which then never finished
- read the whole ATAPI elementary transfer in one asynchronous
request instead, removing cd_read_sector_sync()
- extend the IDE/AHCI qtest coverage of ATAPI CD reads: parametrized
read helper, multi-sector DMA, raw 2352-byte READ CD on both
delivery paths, and a regression test for the deadlock above
Changes since v1:
- MAINTAINERS patch dropped
- spare blank line no longer added and removed within the series
NOTE: 'hw/ide: Don't divide by zero if guest specifies 0 sectors requires'
is not ready for me, skipped from this submission.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
----------------------------------------------------------------
Denis V. Lunev (7):
tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test
tests/qtest/ide-test: add a multi-sector ATAPI DMA read test
tests/qtest/ide-test: cover raw (2352-byte) ATAPI CD reads
tests/qtest/libqos/ahci: support raw (2352-byte) READ CD
tests/qtest/ahci: cover raw (2352-byte) ATAPI CD reads
hw/ide/atapi: read the whole elementary transfer asynchronously
tests/qtest/ahci: regression test for ATAPI read vs. drain
hw/ide/atapi.c | 180 +++++++++++++++-----------------
tests/qtest/ahci-test.c | 134 ++++++++++++++++++++++++
tests/qtest/ide-test.c | 211 ++++++++++++++++++++++++--------------
tests/qtest/libqos/ahci.c | 5 +
tests/qtest/libqos/ahci.h | 2 +
5 files changed, 360 insertions(+), 172 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PULL v2 1/7] tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
@ 2026-08-04 17:00 ` Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 2/7] tests/qtest/ide-test: add a multi-sector ATAPI DMA " Denis V. Lunev
` (7 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
cdrom_pio_impl() and test_cdrom_dma() duplicate the same image setup
and data-integrity check around two different transfer mechanisms.
Fold them into a single cdrom_read_impl(nblocks, flags) helper, with a
CDROM_PIO/CDROM_DMA flag selecting the transfer, so further read
coverage can be added once for both paths.
No functional change: /ide/cdrom/pio, pio_large and dma run exactly
as before.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
tests/qtest/ide-test.c | 145 +++++++++++++++++++----------------------
1 file changed, 67 insertions(+), 78 deletions(-)
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index 721e78170b..c97bc17b67 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -1034,8 +1034,12 @@ static void ide_wait_intr(QTestState *qts, int irq)
g_assert_not_reached();
}
-static void cdrom_pio_impl(int nblocks)
+#define CDROM_PIO 0
+#define CDROM_DMA (1 << 0)
+
+static void cdrom_read_impl(int nblocks, unsigned flags)
{
+ bool dma = flags & CDROM_DMA;
QTestState *qts;
QPCIDevice *dev;
QPCIBar bmdma_bar, ide_bar;
@@ -1063,57 +1067,75 @@ static void cdrom_pio_impl(int nblocks)
dev = get_pci_device(qts, &bmdma_bar, &ide_bar);
qtest_irq_intercept_in(qts, "ioapic");
- /* PACKET command on device 0 */
- qpci_io_writeb(dev, ide_bar, reg_device, 0);
- qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF);
- qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF));
- qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET);
- /* HP0: Check_Status_A State */
- nsleep(qts, 400);
- data = ide_wait_clear(qts, BSY);
- /* HP1: Send_Packet State */
- assert_bit_set(data, DRQ | DRDY);
- assert_bit_clear(data, ERR | DF | BSY);
-
- /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */
- send_scsi_cdb_read10(dev, ide_bar, 0, nblocks);
-
- /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes.
- * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes.
- * We allow an odd limit only when the remaining transfer size is
- * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only
- * request n blocks, so our request size is always even.
- * For this reason, we assume there is never a hanging byte to fetch. */
- g_assert(!(rxsize & 1));
- limit = BYTE_COUNT_LIMIT & ~1;
- for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) {
- size_t offset = i * (limit / 2);
- size_t rem = (rxsize / 2) - offset;
-
- /* HP3: INTRQ_Wait */
- ide_wait_intr(qts, IDE_PRIMARY_IRQ);
+ if (dma) {
+ uintptr_t guest_buf = guest_alloc(&guest_malloc, rxsize);
+ PrdtEntry prdt[1];
+
+ prdt[0].addr = cpu_to_le32(guest_buf);
+ prdt[0].size = cpu_to_le32(rxsize | PRDT_EOT);
+
+ send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_PACKET, 0,
+ nblocks, prdt, ARRAY_SIZE(prdt),
+ send_scsi_cdb_read10);
- /* HP2: Check_Status_B (and clear IRQ) */
+ qtest_memread(qts, guest_buf, rx, rxsize);
+ } else {
+ /* PACKET command on device 0 */
+ qpci_io_writeb(dev, ide_bar, reg_device, 0);
+ qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF);
+ qpci_io_writeb(dev, ide_bar, reg_lba_high,
+ (BYTE_COUNT_LIMIT >> 8 & 0xFF));
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET);
+ /* HP0: Check_Status_A State */
+ nsleep(qts, 400);
data = ide_wait_clear(qts, BSY);
+ /* HP1: Send_Packet State */
assert_bit_set(data, DRQ | DRDY);
assert_bit_clear(data, ERR | DF | BSY);
- /* HP4: Transfer_Data */
- for (j = 0; j < MIN((limit / 2), rem); j++) {
- rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar,
- reg_data));
+ /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */
+ send_scsi_cdb_read10(dev, ide_bar, 0, nblocks);
+
+ /*
+ * Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes.
+ * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes.
+ * We allow an odd limit only when the remaining transfer size is
+ * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only
+ * request n blocks, so our request size is always even.
+ * For this reason, we assume there is never a hanging byte to fetch.
+ */
+ g_assert(!(rxsize & 1));
+ limit = BYTE_COUNT_LIMIT & ~1;
+ for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) {
+ size_t offset = i * (limit / 2);
+ size_t rem = (rxsize / 2) - offset;
+
+ /* HP3: INTRQ_Wait */
+ ide_wait_intr(qts, IDE_PRIMARY_IRQ);
+
+ /* HP2: Check_Status_B (and clear IRQ) */
+ data = ide_wait_clear(qts, BSY);
+ assert_bit_set(data, DRQ | DRDY);
+ assert_bit_clear(data, ERR | DF | BSY);
+
+ /* HP4: Transfer_Data */
+ for (j = 0; j < MIN((limit / 2), rem); j++) {
+ rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar,
+ reg_data));
+ }
}
- }
- /* Check for final completion IRQ */
- ide_wait_intr(qts, IDE_PRIMARY_IRQ);
+ /* Check for final completion IRQ */
+ ide_wait_intr(qts, IDE_PRIMARY_IRQ);
- /* Sanity check final state */
- data = ide_wait_clear(qts, DRQ);
- assert_bit_set(data, DRDY);
- assert_bit_clear(data, DRQ | ERR | DF | BSY);
+ /* Sanity check final state */
+ data = ide_wait_clear(qts, DRQ);
+ assert_bit_set(data, DRDY);
+ assert_bit_clear(data, DRQ | ERR | DF | BSY);
+ }
g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0);
+
g_free(pattern);
g_free(rx);
test_bmdma_teardown(qts);
@@ -1122,51 +1144,18 @@ static void cdrom_pio_impl(int nblocks)
static void test_cdrom_pio(void)
{
- cdrom_pio_impl(1);
+ cdrom_read_impl(1, CDROM_PIO);
}
static void test_cdrom_pio_large(void)
{
/* Test a few loops of the PIO DRQ mechanism. */
- cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE);
+ cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_PIO);
}
-
static void test_cdrom_dma(void)
{
- QTestState *qts;
- static const size_t len = ATAPI_BLOCK_SIZE;
- size_t ret;
- char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16);
- char *rx = g_malloc0(len);
- uintptr_t guest_buf;
- PrdtEntry prdt[1];
- FILE *fh;
-
- qts = ide_test_start(
- "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 "
- "-device ide-cd,drive=sr0,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);
-
- generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE);
- fh = fopen(tmp_path[0], "wb+");
- ret = fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh);
- g_assert_cmpint(ret, ==, 16);
- fclose(fh);
-
- send_dma_request(qts, CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10);
-
- /* Read back data from guest memory into local qtest memory */
- qtest_memread(qts, guest_buf, rx, len);
- g_assert_cmpint(memcmp(pattern, rx, len), ==, 0);
-
- g_free(pattern);
- g_free(rx);
- test_bmdma_teardown(qts);
+ cdrom_read_impl(1, CDROM_DMA);
}
int main(int argc, char **argv)
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PULL v2 2/7] tests/qtest/ide-test: add a multi-sector ATAPI DMA read test
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 1/7] tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test Denis V. Lunev
@ 2026-08-04 17:00 ` Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 3/7] tests/qtest/ide-test: cover raw (2352-byte) ATAPI CD reads Denis V. Lunev
` (6 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
test_cdrom_pio_large() already exercises a multi-sector PIO read.
Add the DMA counterpart through the same cdrom_read_impl() helper so
the multi-block ATAPI DMA read path gets equivalent coverage.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
tests/qtest/ide-test.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index c97bc17b67..6e59197566 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -1158,6 +1158,11 @@ static void test_cdrom_dma(void)
cdrom_read_impl(1, CDROM_DMA);
}
+static void test_cdrom_dma_large(void)
+{
+ cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_DMA);
+}
+
int main(int argc, char **argv)
{
const char *base;
@@ -1217,6 +1222,7 @@ int main(int argc, char **argv)
qtest_add_func("/ide/cdrom/pio", test_cdrom_pio);
qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large);
qtest_add_func("/ide/cdrom/dma", test_cdrom_dma);
+ qtest_add_func("/ide/cdrom/dma_large", test_cdrom_dma_large);
ret = g_test_run();
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PULL v2 3/7] tests/qtest/ide-test: cover raw (2352-byte) ATAPI CD reads
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 1/7] tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 2/7] tests/qtest/ide-test: add a multi-sector ATAPI DMA " Denis V. Lunev
@ 2026-08-04 17:00 ` Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 4/7] tests/qtest/libqos/ahci: support raw (2352-byte) READ CD Denis V. Lunev
` (5 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
READ CD with the field-selector set to 0xf8 returns full 2352-byte
raw sectors (sync + header + 2048 data + EDC/ECC), driving the ATAPI
raw read path that READ10 never touches. Add a send_scsi_cdb_read_cd()
helper and a CDROM_RAW flag to cdrom_read_impl(), then exercise both
PIO and DMA. The PIO case uses a byte-count limit spanning several
raw sectors so the device must rebuffer mid-burst, and each sector's
2048-byte payload is verified at its in-sector offset.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
tests/qtest/ide-test.c | 76 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 70 insertions(+), 6 deletions(-)
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index 6e59197566..b36b0f8875 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -48,6 +48,10 @@
#define ATAPI_BLOCK_SIZE 2048
+/* Raw READ CD sector: 12 sync + 4 header + 2048 data + 288 EDC/ECC. */
+#define ATAPI_RAW_SIZE 2352
+#define ATAPI_RAW_DATA 16
+
/* How many bytes to receive via ATAPI PIO at one time.
* Must be less than 0xFFFF. */
#define BYTE_COUNT_LIMIT 5120
@@ -982,6 +986,41 @@ static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar,
}
}
+typedef struct ReadCDCDB {
+ uint8_t opcode;
+ uint8_t sector_type;
+ uint32_t lba;
+ uint8_t length[3];
+ uint8_t main_channel;
+ uint8_t sub_channel;
+ uint8_t control;
+} __attribute__((__packed__)) ReadCDCDB;
+
+static void send_scsi_cdb_read_cd(QPCIDevice *dev, QPCIBar ide_bar,
+ uint64_t lba, int nblocks)
+{
+ ReadCDCDB pkt = { };
+ int i;
+
+ g_assert_cmpint(lba, <=, UINT32_MAX);
+ g_assert_cmpint(nblocks, >=, 0);
+ g_assert_cmpint(nblocks, <=, 0xffffff);
+
+ /* Construct SCSI CDB packet */
+ pkt.opcode = 0xbe;
+ pkt.lba = cpu_to_be32(lba);
+ pkt.length[0] = (nblocks >> 16) & 0xff;
+ pkt.length[1] = (nblocks >> 8) & 0xff;
+ pkt.length[2] = nblocks & 0xff;
+ pkt.main_channel = 0xf8; /* sync + headers + user data + EDC/ECC: 2352 */
+
+ /* Send Packet */
+ for (i = 0; i < sizeof(ReadCDCDB) / 2; i++) {
+ qpci_io_writew(dev, ide_bar, reg_data,
+ le16_to_cpu(((uint16_t *)&pkt)[i]));
+ }
+}
+
static void nsleep(QTestState *qts, int64_t nsecs)
{
const struct timespec val = { .tv_nsec = nsecs };
@@ -1036,10 +1075,12 @@ static void ide_wait_intr(QTestState *qts, int irq)
#define CDROM_PIO 0
#define CDROM_DMA (1 << 0)
+#define CDROM_RAW (1 << 1)
static void cdrom_read_impl(int nblocks, unsigned flags)
{
bool dma = flags & CDROM_DMA;
+ bool raw = flags & CDROM_RAW;
QTestState *qts;
QPCIDevice *dev;
QPCIBar bmdma_bar, ide_bar;
@@ -1047,8 +1088,11 @@ static void cdrom_read_impl(int nblocks, unsigned flags)
int patt_blocks = MAX(16, nblocks);
size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks;
char *pattern = g_malloc(patt_len);
- size_t rxsize = ATAPI_BLOCK_SIZE * nblocks;
+ unsigned xfer = raw ? ATAPI_RAW_SIZE : ATAPI_BLOCK_SIZE;
+ size_t rxsize = xfer * nblocks;
uint16_t *rx = g_malloc0(rxsize);
+ void (*send_cdb)(QPCIDevice *, QPCIBar, uint64_t, int) =
+ raw ? send_scsi_cdb_read_cd : send_scsi_cdb_read10;
int i, j;
uint8_t data;
uint16_t limit;
@@ -1075,8 +1119,7 @@ static void cdrom_read_impl(int nblocks, unsigned flags)
prdt[0].size = cpu_to_le32(rxsize | PRDT_EOT);
send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_PACKET, 0,
- nblocks, prdt, ARRAY_SIZE(prdt),
- send_scsi_cdb_read10);
+ nblocks, prdt, ARRAY_SIZE(prdt), send_cdb);
qtest_memread(qts, guest_buf, rx, rxsize);
} else {
@@ -1093,8 +1136,7 @@ static void cdrom_read_impl(int nblocks, unsigned flags)
assert_bit_set(data, DRQ | DRDY);
assert_bit_clear(data, ERR | DF | BSY);
- /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */
- send_scsi_cdb_read10(dev, ide_bar, 0, nblocks);
+ send_cdb(dev, ide_bar, 0, nblocks);
/*
* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes.
@@ -1134,7 +1176,17 @@ static void cdrom_read_impl(int nblocks, unsigned flags)
assert_bit_clear(data, DRQ | ERR | DF | BSY);
}
- g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0);
+ if (raw) {
+ /* The 2048-byte payload of each raw sector sits past its header. */
+ for (i = 0; i < nblocks; i++) {
+ uint8_t *sec = (uint8_t *)rx + i * ATAPI_RAW_SIZE + ATAPI_RAW_DATA;
+
+ g_assert_cmpint(memcmp(sec, pattern + i * ATAPI_BLOCK_SIZE,
+ ATAPI_BLOCK_SIZE), ==, 0);
+ }
+ } else {
+ g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0);
+ }
g_free(pattern);
g_free(rx);
@@ -1163,6 +1215,16 @@ static void test_cdrom_dma_large(void)
cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_DMA);
}
+static void test_cdrom_pio_raw(void)
+{
+ cdrom_read_impl(4, CDROM_RAW);
+}
+
+static void test_cdrom_dma_raw(void)
+{
+ cdrom_read_impl(4, CDROM_DMA | CDROM_RAW);
+}
+
int main(int argc, char **argv)
{
const char *base;
@@ -1223,6 +1285,8 @@ int main(int argc, char **argv)
qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large);
qtest_add_func("/ide/cdrom/dma", test_cdrom_dma);
qtest_add_func("/ide/cdrom/dma_large", test_cdrom_dma_large);
+ qtest_add_func("/ide/cdrom/pio_raw", test_cdrom_pio_raw);
+ qtest_add_func("/ide/cdrom/dma_raw", test_cdrom_dma_raw);
ret = g_test_run();
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PULL v2 4/7] tests/qtest/libqos/ahci: support raw (2352-byte) READ CD
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
` (2 preceding siblings ...)
2026-08-04 17:00 ` [PULL v2 3/7] tests/qtest/ide-test: cover raw (2352-byte) ATAPI CD reads Denis V. Lunev
@ 2026-08-04 17:00 ` Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 5/7] tests/qtest/ahci: cover raw (2352-byte) ATAPI CD reads Denis V. Lunev
` (4 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
ahci_exec() always builds ATAPI commands with a 2048-byte logical
sector size, so it cannot drive a READ CD that returns full 2352-byte
raw sectors. Add an atapi_raw option that sets the READ CD
field-selector to 0xf8 and the command's sector size to 2352 before
the transfer is sized, so the derived block count stays correct while
the buffer and byte counts cover the raw sectors.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
tests/qtest/libqos/ahci.c | 5 +++++
tests/qtest/libqos/ahci.h | 2 ++
2 files changed, 7 insertions(+)
diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c
index 0621a6c477..e3019a1eca 100644
--- a/tests/qtest/libqos/ahci.c
+++ b/tests/qtest/libqos/ahci.c
@@ -707,6 +707,11 @@ void ahci_exec(AHCIQState *ahci, uint8_t port,
if (opts->atapi) {
uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE;
cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma);
+ if (opts->atapi_raw) {
+ /* request full 2352-byte raw sectors; sector_size must match */
+ cmd->atapi_cmd[9] = 0xf8;
+ cmd->sector_size = ATAPI_RAW_SECTOR_SIZE;
+ }
} else {
cmd = ahci_command_create(op);
}
diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h
index a0487a1557..2a48a7523d 100644
--- a/tests/qtest/libqos/ahci.h
+++ b/tests/qtest/libqos/ahci.h
@@ -242,6 +242,7 @@
#define AHCI_SECTOR_SIZE (512)
#define ATAPI_SECTOR_SIZE (2048)
+#define ATAPI_RAW_SECTOR_SIZE (2352)
#define AHCI_SIGNATURE_CDROM (0xeb140101)
#define AHCI_SIGNATURE_DISK (0x00000101)
@@ -485,6 +486,7 @@ typedef struct AHCIOpts {
uint64_t buffer; /* Pointer to source or destination guest buffer */
bool atapi; /* ATAPI command? */
bool atapi_dma; /* Use DMA for ATAPI? */
+ bool atapi_raw; /* READ CD returning 2352-byte raw sectors */
bool error;
int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PULL v2 5/7] tests/qtest/ahci: cover raw (2352-byte) ATAPI CD reads
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
` (3 preceding siblings ...)
2026-08-04 17:00 ` [PULL v2 4/7] tests/qtest/libqos/ahci: support raw (2352-byte) READ CD Denis V. Lunev
@ 2026-08-04 17:00 ` Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 6/7] hw/ide/atapi: read the whole elementary transfer asynchronously Denis V. Lunev
` (3 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
Add /ahci/cdrom/{pio,dma}/raw: read several sectors with READ CD in
raw mode (atapi_raw), so the ATAPI 2352-byte unpack path is exercised
through the AHCI delivery, which IDE coverage does not reach. Each
sector's 2048-byte payload is verified at its in-sector offset.
The PIO case uses a byte-count limit of one raw sector per DRQ burst:
libqos asserts a one-sector PIO transfer, and the multi-sector unpack
loop is already covered by the IDE raw test.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
tests/qtest/ahci-test.c | 68 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c
index 44799eea15..58bc04b3ef 100644
--- a/tests/qtest/ahci-test.c
+++ b/tests/qtest/ahci-test.c
@@ -1565,6 +1565,31 @@ static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd,
return 0;
}
+static int ahci_cb_cmp_raw(AHCIQState *ahci, AHCICommand *cmd,
+ const AHCIOpts *opts)
+{
+ unsigned char *tx = opts->opaque;
+ unsigned char *rx;
+ unsigned i, nsectors;
+
+ if (!opts->size) {
+ return 0;
+ }
+
+ nsectors = opts->size / ATAPI_RAW_SECTOR_SIZE;
+ rx = g_malloc0(opts->size);
+ qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size);
+ /* Each raw sector carries its 2048-byte payload past a 16-byte header. */
+ for (i = 0; i < nsectors; i++) {
+ g_assert_cmphex(memcmp(rx + i * ATAPI_RAW_SECTOR_SIZE + 16,
+ tx + i * ATAPI_SECTOR_SIZE,
+ ATAPI_SECTOR_SIZE), ==, 0);
+ }
+ g_free(rx);
+
+ return 0;
+}
+
static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd,
bool override_bcl, uint16_t bcl)
{
@@ -1625,6 +1650,47 @@ static void test_cdrom_pio_multi(void)
ahci_test_cdrom_read10(3, false);
}
+static void ahci_test_cdrom_raw(int nsectors, bool dma)
+{
+ AHCIQState *ahci;
+ unsigned char *tx;
+ char *iso;
+ int fd;
+ AHCIOpts opts = {
+ .size = (uint64_t)ATAPI_RAW_SECTOR_SIZE * nsectors,
+ .atapi = true,
+ .atapi_dma = dma,
+ .atapi_raw = true,
+ .set_bcl = true,
+ .bcl = ATAPI_RAW_SECTOR_SIZE, /* one raw sector per DRQ burst */
+ .post_cb = ahci_cb_cmp_raw,
+ };
+ uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * (nsectors + 1);
+
+ fd = prepare_iso(iso_size, &tx, &iso);
+ opts.opaque = tx;
+
+ ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw "
+ "-M q35 "
+ "-device ide-cd,drive=drive0 ", iso);
+
+ ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_CD, &opts);
+
+ g_free(tx);
+ ahci_shutdown(ahci);
+ remove_iso(fd, iso);
+}
+
+static void test_cdrom_dma_raw(void)
+{
+ ahci_test_cdrom_raw(3, true);
+}
+
+static void test_cdrom_pio_raw(void)
+{
+ ahci_test_cdrom_raw(3, false);
+}
+
/*
* Regression test: a buffered ATAPI read completing after a command
* engine restart must not dereference the cleared cur_cmd. Cover both
@@ -2100,8 +2166,10 @@ int main(int argc, char **argv)
qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma);
qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi);
+ qtest_add_func("/ahci/cdrom/dma/raw", test_cdrom_dma_raw);
qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio);
qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi);
+ qtest_add_func("/ahci/cdrom/pio/raw", test_cdrom_pio_raw);
qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl);
qtest_add_func("/ahci/cdrom/eject", test_atapi_tray);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PULL v2 6/7] hw/ide/atapi: read the whole elementary transfer asynchronously
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
` (4 preceding siblings ...)
2026-08-04 17:00 ` [PULL v2 5/7] tests/qtest/ahci: cover raw (2352-byte) ATAPI CD reads Denis V. Lunev
@ 2026-08-04 17:00 ` Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 7/7] tests/qtest/ahci: regression test for ATAPI read vs. drain Denis V. Lunev
` (2 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
An ATAPI PIO read whose byte-count limit spans more than one CD sector
must fetch the later sectors of a DRQ burst from inside the completion
of the first, asynchronous read. cd_read_sector_sync() did this with a
synchronous blk_pread(), which runs blk_wait_while_drained() before
issuing the request.
If a drain is in progress when that completion runs -- as happens when
a guest reset reaches virtio_blk_stop_ioeventfd() ->
bdrv_drain_all_begin() while an ATAPI read is in flight on the same
QEMU -- the nested read is queued until the drained section ends while
the outer completion still holds blk->in_flight. bdrv_drain_all_begin()
then waits forever for that in_flight count to drop: the main loop is
wedged in the drain with the BQL held, and every other QMP/monitor
operation blocks behind it.
Read the whole elementary transfer in a single asynchronous request up
front instead, so no read is ever issued in the middle of a burst.
cd_read_sector() now reads all the sectors a burst spans (the raw
2352-byte case is unpacked in place on completion) and
cd_read_sector_sync() is removed. The DMA path already batched its
reads and is unchanged.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
hw/ide/atapi.c | 180 +++++++++++++++++++++++--------------------------
1 file changed, 84 insertions(+), 96 deletions(-)
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index a42b748521..0ea149ad8c 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -88,46 +88,14 @@ static void cd_data_to_raw(uint8_t *buf, int lba)
memset(buf, 0, 288);
}
-static int
-cd_read_sector_sync(IDEState *s)
-{
- int ret;
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ);
-
- trace_cd_read_sector_sync(s->lba);
-
- switch (s->cd_sector_size) {
- case 2048:
- ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS,
- ATAPI_SECTOR_SIZE, s->io_buffer, 0);
- break;
- case 2352:
- ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS,
- ATAPI_SECTOR_SIZE, s->io_buffer + 16, 0);
- if (ret >= 0) {
- cd_data_to_raw(s->io_buffer, s->lba);
- }
- break;
- default:
- block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
- return -EIO;
- }
-
- if (ret < 0) {
- block_acct_failed(blk_get_stats(s->blk), &s->acct);
- } else {
- block_acct_done(blk_get_stats(s->blk), &s->acct);
- s->lba++;
- s->io_buffer_index = 0;
- }
-
- return ret;
-}
-
static void cd_read_sector_cb(void *opaque, int ret)
{
IDEState *s = opaque;
+ int et = s->elementary_transfer_size;
+ int skip = s->io_buffer_index;
+ int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size);
+ uint8_t *buf;
+ int i;
trace_cd_read_sector_cb(s->lba, ret);
@@ -140,34 +108,64 @@ static void cd_read_sector_cb(void *opaque, int ret)
block_acct_done(blk_get_stats(s->blk), &s->acct);
if (s->cd_sector_size == 2352) {
- cd_data_to_raw(s->io_buffer, s->lba);
+ /* unpack back-to-front so a sector never clobbers an unmoved one */
+ for (i = nsec - 1; i >= 0; i--) {
+ memmove(s->io_buffer + i * 2352 + 16, s->io_buffer + i * 2048,
+ ATAPI_SECTOR_SIZE);
+ cd_data_to_raw(s->io_buffer + i * 2352, s->lba + i);
+ }
}
- s->lba++;
- s->io_buffer_index = 0;
s->status &= ~BUSY_STAT;
- ide_atapi_cmd_reply_end(s);
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+ s->lcyl = et & 0xff;
+ s->hcyl = (et >> 8) & 0xff;
+ ide_bus_set_irq(s->bus);
+
+ /* a boundary sector shared with the next burst is re-read there */
+ buf = s->io_buffer + skip;
+ s->packet_transfer_size -= et;
+ s->lba += (skip + et) / s->cd_sector_size;
+ s->io_buffer_index = (skip + et) % s->cd_sector_size;
+ s->elementary_transfer_size = 0;
+
+ if (ide_transfer_start_norecurse(s, buf, et, ide_atapi_cmd_reply_end)) {
+ ide_atapi_cmd_reply_end(s);
+ }
}
+/*
+ * Read the whole elementary transfer (one DRQ burst) in a single async
+ * request. No read is issued mid-burst, so unlike the old synchronous
+ * rebuffer it cannot deadlock against a concurrent drain.
+ */
static int cd_read_sector(IDEState *s)
{
- void *buf;
+ int et = s->elementary_transfer_size;
+ int skip = s->io_buffer_index;
+ int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size);
if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) {
block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
return -EINVAL;
}
- buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer;
- qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE);
+ /* a burst is bounded by the byte count limit, so it fits io_buffer */
+ assert(nsec * s->cd_sector_size <= s->io_buffer_total_len);
+
+ /*
+ * Read the payload packed at the front of io_buffer; the 2352 raw case is
+ * unpacked into place on completion.
+ */
+ qemu_iovec_init_buf(&s->qiov, s->io_buffer, nsec * ATAPI_SECTOR_SIZE);
trace_cd_read_sector(s->lba);
block_acct_start(blk_get_stats(s->blk), &s->acct,
- ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ);
+ nsec * ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ);
- ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4,
+ ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, nsec * 4,
cd_read_sector_cb, s);
s->status |= BUSY_STAT;
@@ -222,59 +220,49 @@ static uint16_t atapi_byte_count_limit(IDEState *s)
void ide_atapi_cmd_reply_end(IDEState *s)
{
int byte_count_limit, size, ret;
- while (s->packet_transfer_size > 0) {
- trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size,
- s->elementary_transfer_size,
- s->io_buffer_index);
-
- /* see if a new sector must be read */
- if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
- if (!s->elementary_transfer_size) {
- ret = cd_read_sector(s);
- if (ret < 0) {
- ide_atapi_io_error(s, ret);
- }
- return;
- } else {
- /* rebuffering within an elementary transfer is
- * only possible with a sync request because we
- * end up with a race condition otherwise */
- ret = cd_read_sector_sync(s);
- if (ret < 0) {
- ide_atapi_io_error(s, ret);
- return;
- }
+
+ trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size,
+ s->elementary_transfer_size,
+ s->io_buffer_index);
+
+ if (s->lba != -1 && s->packet_transfer_size > 0) {
+ byte_count_limit = atapi_byte_count_limit(s);
+ trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit);
+ size = s->packet_transfer_size;
+ if (size > byte_count_limit) {
+ /* byte count limit must be even if this case */
+ if (byte_count_limit & 1) {
+ byte_count_limit--;
}
+ size = byte_count_limit;
}
- if (s->elementary_transfer_size > 0) {
- /* there are some data left to transmit in this elementary
- transfer */
- size = s->cd_sector_size - s->io_buffer_index;
- if (size > s->elementary_transfer_size)
- size = s->elementary_transfer_size;
- } else {
- /* a new transfer is needed */
- s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
- ide_bus_set_irq(s->bus);
- byte_count_limit = atapi_byte_count_limit(s);
- trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit);
- size = s->packet_transfer_size;
- if (size > byte_count_limit) {
- /* byte count limit must be even if this case */
- if (byte_count_limit & 1)
- byte_count_limit--;
- size = byte_count_limit;
- }
- s->lcyl = size & 0xff;
- s->hcyl = size >> 8;
- s->elementary_transfer_size = size;
- /* we cannot transmit more than one sector at a time */
- if (s->lba != -1) {
- if (size > (s->cd_sector_size - s->io_buffer_index))
- size = (s->cd_sector_size - s->io_buffer_index);
+ s->elementary_transfer_size = size;
+ ret = cd_read_sector(s);
+ if (ret < 0) {
+ ide_atapi_io_error(s, ret);
+ }
+ return;
+ }
+
+ while (s->packet_transfer_size > 0) {
+ /* a new transfer is needed */
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+ ide_bus_set_irq(s->bus);
+ byte_count_limit = atapi_byte_count_limit(s);
+ trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit);
+ size = s->packet_transfer_size;
+ if (size > byte_count_limit) {
+ /* byte count limit must be even if this case */
+ if (byte_count_limit & 1) {
+ byte_count_limit--;
}
- trace_ide_atapi_cmd_reply_end_new(s, s->status);
+ size = byte_count_limit;
}
+ s->lcyl = size & 0xff;
+ s->hcyl = size >> 8;
+ s->elementary_transfer_size = size;
+ trace_ide_atapi_cmd_reply_end_new(s, s->status);
+
s->packet_transfer_size -= size;
s->elementary_transfer_size -= size;
s->io_buffer_index += size;
@@ -329,7 +317,7 @@ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
s->lba = lba;
s->packet_transfer_size = nb_sectors * sector_size;
s->elementary_transfer_size = 0;
- s->io_buffer_index = sector_size;
+ s->io_buffer_index = 0;
s->cd_sector_size = sector_size;
ide_atapi_cmd_reply_end(s);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PULL v2 7/7] tests/qtest/ahci: regression test for ATAPI read vs. drain
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
` (5 preceding siblings ...)
2026-08-04 17:00 ` [PULL v2 6/7] hw/ide/atapi: read the whole elementary transfer asynchronously Denis V. Lunev
@ 2026-08-04 17:00 ` Denis V. Lunev
2026-08-05 0:55 ` [PULL v2 0/7] IDE patches Stefan Hajnoczi
2026-08-12 16:19 ` Richard Henderson
8 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-04 17:00 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, John Snow, Denis V. Lunev, Stefan Hajnoczi,
Philippe Mathieu-Daudé
Add /ahci/cdrom/drain/{pio,dma}: issue a multi-sector ATAPI read whose
byte-count limit spans two sectors so the device must rebuffer in the
middle of the DRQ burst, hold the backend read in flight with a
blkdebug delay, and fire x-blockdev-set-iothread -- which runs
bdrv_drain_all_begin() exactly like a guest reset does through
virtio_blk_stop_ioeventfd().
On the unfixed PIO path the nested sector fetch is queued behind the
drain and the main loop wedges, so the test hangs. The DMA variant
never rebuffers and serves as a sanity twin.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
tests/qtest/ahci-test.c | 66 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c
index 58bc04b3ef..71d23fe56a 100644
--- a/tests/qtest/ahci-test.c
+++ b/tests/qtest/ahci-test.c
@@ -1754,6 +1754,70 @@ static void test_atapi_engine_restart_dma(void)
test_atapi_engine_restart_in_flight(true);
}
+/*
+ * Regression test: a multi-sector ATAPI read fetches its later sectors from
+ * inside the first read's completion; a concurrent drain (as a guest reset
+ * triggers via bdrv_drain_all_begin) must not wedge on that nested read.
+ * blkdebug keeps the read in flight across x-blockdev-set-iothread.
+ */
+static void test_atapi_drain_in_flight(bool dma)
+{
+ AHCIQState *ahci;
+ AHCICommand *cmd;
+ unsigned char *tx;
+ char *iso;
+ int fd;
+ uint8_t port;
+ uint64_t buffer;
+ uint16_t bcl = ATAPI_SECTOR_SIZE * 2;
+ uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * 3;
+
+ fd = prepare_iso(iso_size, &tx, &iso);
+
+ /* 1s read delay: a wide margin so the drain starts before it completes */
+ ahci = ahci_boot_and_enable(
+ "-blockdev driver=file,node-name=file0,filename=%s,read-only=on "
+ "-blockdev driver=blkdebug,node-name=cd0,image=file0,read-only=on,"
+ "inject-error.0.event=none,inject-error.0.iotype=read,"
+ "inject-error.0.errno=0,inject-error.0.delay-ns=1000000000 "
+ "-M q35 "
+ "-device ide-cd,drive=cd0 ", iso);
+ port = ahci_port_select(ahci);
+
+ buffer = ahci_alloc(ahci, bcl);
+ qtest_memset(ahci->parent->qts, buffer, 0x00, bcl);
+
+ cmd = ahci_atapi_command_create(CMD_ATAPI_READ_10, bcl, dma);
+ ahci_command_adjust(cmd, 0, buffer, bcl, 0);
+ ahci_command_commit(ahci, cmd, port);
+ ahci_command_issue_async(ahci, cmd);
+
+ /* Drain (all nodes) while the delayed read is still in flight. */
+ qtest_qmp_assert_success(ahci->parent->qts,
+ "{ 'execute': 'x-blockdev-set-iothread',"
+ " 'arguments': { 'node-name': 'cd0', 'iothread': null,"
+ " 'force': true } }");
+
+ /* Round-trip through the device to confirm qemu is still alive. */
+ ahci_px_rreg(ahci, port, AHCI_PX_TFD);
+
+ ahci_command_free(cmd);
+ ahci_free(ahci, buffer);
+ g_free(tx);
+ ahci_shutdown(ahci);
+ remove_iso(fd, iso);
+}
+
+static void test_atapi_drain_pio(void)
+{
+ test_atapi_drain_in_flight(false);
+}
+
+static void test_atapi_drain_dma(void)
+{
+ test_atapi_drain_in_flight(true);
+}
+
/* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0
* completes as a NOP instead of erroring out. */
static void test_atapi_bcl(void)
@@ -2177,6 +2241,8 @@ int main(int argc, char **argv)
test_atapi_engine_restart_pio);
qtest_add_func("/ahci/cdrom/engine_restart/dma",
test_atapi_engine_restart_dma);
+ qtest_add_func("/ahci/cdrom/drain/pio", test_atapi_drain_pio);
+ qtest_add_func("/ahci/cdrom/drain/dma", test_atapi_drain_dma);
ret = g_test_run();
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PULL v2 0/7] IDE patches
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
` (6 preceding siblings ...)
2026-08-04 17:00 ` [PULL v2 7/7] tests/qtest/ahci: regression test for ATAPI read vs. drain Denis V. Lunev
@ 2026-08-05 0:55 ` Stefan Hajnoczi
2026-08-12 12:48 ` Denis V. Lunev
2026-08-12 16:19 ` Richard Henderson
8 siblings, 1 reply; 13+ messages in thread
From: Stefan Hajnoczi @ 2026-08-05 0:55 UTC (permalink / raw)
To: Denis V. Lunev
Cc: qemu-devel, qemu-block, John Snow, Denis V. Lunev,
Stefan Hajnoczi, Philippe Mathieu-Daudé
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PULL v2 0/7] IDE patches
2026-08-05 0:55 ` [PULL v2 0/7] IDE patches Stefan Hajnoczi
@ 2026-08-12 12:48 ` Denis V. Lunev
2026-08-12 14:32 ` Stefan Hajnoczi
0 siblings, 1 reply; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-12 12:48 UTC (permalink / raw)
To: Stefan Hajnoczi, Denis V. Lunev
Cc: qemu-devel, qemu-block, John Snow, Philippe Mathieu-Daudé
On 8/5/26 02:55, Stefan Hajnoczi wrote:
> Applied, thanks.
>
> Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
Hi, Stefan!
It is kinda strange but I do not see this series neither
in master, no in staging today.
The head for me is
commit e8d693e12af9cbb89d724baadfcc08559669e279 (upstream/staging,
upstream/master, upstream/HEAD)
Author: Stefan Hajnoczi <stefanha@redhat.com>
Date: Tue Aug 11 14:31:44 2026 -0400
I see only this patch merged:
athena ~/src/qemu $ git log --author openvz.org hw/ide/
commit d9f78431d8ebdc2d03ad74461138c1c9eb076aa5
Author: Denis V. Lunev <den@openvz.org>
Date: Fri Jun 19 13:21:49 2026 +0200
hw/ide/ahci: cancel in-flight buffered reads on command engine restart
Does something missed/goes wrong?
Thank you in advance,
Den
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PULL v2 0/7] IDE patches
2026-08-12 12:48 ` Denis V. Lunev
@ 2026-08-12 14:32 ` Stefan Hajnoczi
2026-08-12 14:34 ` Denis V. Lunev
0 siblings, 1 reply; 13+ messages in thread
From: Stefan Hajnoczi @ 2026-08-12 14:32 UTC (permalink / raw)
To: Denis V. Lunev
Cc: Denis V. Lunev, qemu-devel, qemu-block, John Snow,
Philippe Mathieu-Daudé
[-- Attachment #1: Type: text/plain, Size: 1177 bytes --]
On Wed, Aug 12, 2026 at 02:48:45PM +0200, Denis V. Lunev wrote:
> On 8/5/26 02:55, Stefan Hajnoczi wrote:
> > Applied, thanks.
> >
> > Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
> Hi, Stefan!
>
> It is kinda strange but I do not see this series neither
> in master, no in staging today.
>
> The head for me is
>
> commit e8d693e12af9cbb89d724baadfcc08559669e279 (upstream/staging,
> upstream/master, upstream/HEAD)
> Author: Stefan Hajnoczi <stefanha@redhat.com>
> Date: Tue Aug 11 14:31:44 2026 -0400
>
> I see only this patch merged:
>
> athena ~/src/qemu $ git log --author openvz.org hw/ide/
> commit d9f78431d8ebdc2d03ad74461138c1c9eb076aa5
> Author: Denis V. Lunev <den@openvz.org>
> Date: Fri Jun 19 13:21:49 2026 +0200
>
> hw/ide/ahci: cancel in-flight buffered reads on command engine restart
>
> Does something missed/goes wrong?
I see the same thing. Sorry, I must have messed up when building the
staging tree and dropped your series.
Richard Henderson is handling qemu.git pull requests for the 11.2
release and I've asked him to merge your series.
Stefan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PULL v2 0/7] IDE patches
2026-08-12 14:32 ` Stefan Hajnoczi
@ 2026-08-12 14:34 ` Denis V. Lunev
0 siblings, 0 replies; 13+ messages in thread
From: Denis V. Lunev @ 2026-08-12 14:34 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Denis V. Lunev, qemu-devel, qemu-block, John Snow,
Philippe Mathieu-Daudé
On 8/12/26 16:32, Stefan Hajnoczi wrote:
> On Wed, Aug 12, 2026 at 02:48:45PM +0200, Denis V. Lunev wrote:
>> On 8/5/26 02:55, Stefan Hajnoczi wrote:
>>> Applied, thanks.
>>>
>>> Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
>> Hi, Stefan!
>>
>> It is kinda strange but I do not see this series neither
>> in master, no in staging today.
>>
>> The head for me is
>>
>> commit e8d693e12af9cbb89d724baadfcc08559669e279 (upstream/staging,
>> upstream/master, upstream/HEAD)
>> Author: Stefan Hajnoczi <stefanha@redhat.com>
>> Date: Tue Aug 11 14:31:44 2026 -0400
>>
>> I see only this patch merged:
>>
>> athena ~/src/qemu $ git log --author openvz.org hw/ide/
>> commit d9f78431d8ebdc2d03ad74461138c1c9eb076aa5
>> Author: Denis V. Lunev <den@openvz.org>
>> Date: Fri Jun 19 13:21:49 2026 +0200
>>
>> hw/ide/ahci: cancel in-flight buffered reads on command engine restart
>>
>> Does something missed/goes wrong?
> I see the same thing. Sorry, I must have messed up when building the
> staging tree and dropped your series.
>
> Richard Henderson is handling qemu.git pull requests for the 11.2
> release and I've asked him to merge your series.
>
> Stefan
Thanks a lot!
Den
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PULL v2 0/7] IDE patches
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
` (7 preceding siblings ...)
2026-08-05 0:55 ` [PULL v2 0/7] IDE patches Stefan Hajnoczi
@ 2026-08-12 16:19 ` Richard Henderson
8 siblings, 0 replies; 13+ messages in thread
From: Richard Henderson @ 2026-08-12 16:19 UTC (permalink / raw)
To: Denis V. Lunev, qemu-devel
Cc: qemu-block, John Snow, Stefan Hajnoczi,
Philippe Mathieu-Daudé
On 8/4/26 10:00, Denis V. Lunev wrote:
> The following changes since commit e1705a25aff35635c360bbaba4c2731d019a422a:
>
> Update version for v11.1.0-rc2 release (2026-07-29 06:16:08 -0400)
>
> are available in the Git repository at:
>
> https://gitlab.com/dlunev/qemu tags/pull-ide-2026-08-04
>
> for you to fetch changes up to 9a249de6ae86124b436b5e839e285aeb333ea8c0:
>
> tests/qtest/ahci: regression test for ATAPI read vs. drain (2026-08-04 18:40:34 +0200)
fatal: couldn't find remote ref tags/pull-ide-2026-08-04
fatal: remote error: upload-pack: not our ref 9a249de6ae86124b436b5e839e285aeb333ea8c0
Did you remove the tag? In any case, please resend.
r~
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-08-12 16:19 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 17:00 [PULL v2 0/7] IDE patches Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 1/7] tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 2/7] tests/qtest/ide-test: add a multi-sector ATAPI DMA " Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 3/7] tests/qtest/ide-test: cover raw (2352-byte) ATAPI CD reads Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 4/7] tests/qtest/libqos/ahci: support raw (2352-byte) READ CD Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 5/7] tests/qtest/ahci: cover raw (2352-byte) ATAPI CD reads Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 6/7] hw/ide/atapi: read the whole elementary transfer asynchronously Denis V. Lunev
2026-08-04 17:00 ` [PULL v2 7/7] tests/qtest/ahci: regression test for ATAPI read vs. drain Denis V. Lunev
2026-08-05 0:55 ` [PULL v2 0/7] IDE patches Stefan Hajnoczi
2026-08-12 12:48 ` Denis V. Lunev
2026-08-12 14:32 ` Stefan Hajnoczi
2026-08-12 14:34 ` Denis V. Lunev
2026-08-12 16:19 ` Richard Henderson
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.