This should be a straightforward DMA cancel. I added some more traces;
# After the 0x35 command write:
ide_exec_cmd IDE exec cmd: bus 0x561808b0ecc0; state 0x561808b0f118; cmd 0x35
ide_sector_start_dma IDEState 0x561808b0f118;
ide_start_dma IDEState 0x561808b0f118;
# After the 0xe1 bmdma kick:
ide_dma_cb_entry IDEState 0x561808b0f118; ret 0;
ide_dma_cb IDEState 0x561808b0f118; sector_num=1 n=259 cmd=DMA WRITE
ide_dma_cb_next IDEState 0x561808b0f118;
So far, pretty normal. IDE calls the HBA's DMA start, but the HBA doesn't have DMA enabled, so it stalls. Later, when we turn on DMA, the HBA engages the DMA callback and sets up the first transfer. This sets s->bus->dma->aiocb.
... The DMA simply re-schedules itself (?) when iov.size is zero. unfortunately for us, that means that the original point of scheduling the drain doesn't work, because the DMA never returns all the way to the IDE device emulation code.
The qtest reproducers are so nice.
writel 0x0 0xffffffff
outw 0x171 0x32a
features := 0x2a b8cb
count := 0x03; b8cb
outw 0x176 0x3570
device := 0x70 (select device1) b8cb
command := 0x35 (DMA WRITE EXT) 8f98
outl 0xcf8 0x80000903
outl 0xcfc 0x4e002700
outl 0xcf8 0x80000920
outb 0xcfc 0x5e
outb 0x58 0xe1
DMA READ ^ ^ DMA Start
^ DMA Cancel
bmdma_cmd_writeb val = 0xe1 [1110 0001]
outw 0x57 0x0
bmdma_cmd_writeb val = 0x00 [0000 0000]
EOF
This should be a straightforward DMA cancel. I added some more traces;
# After the 0x35 command write: start_dma IDEState 0x561808b0f118;
ide_exec_cmd IDE exec cmd: bus 0x561808b0ecc0; state 0x561808b0f118; cmd 0x35
ide_sector_
ide_start_dma IDEState 0x561808b0f118;
# After the 0xe1 bmdma kick:
ide_dma_cb_entry IDEState 0x561808b0f118; ret 0;
ide_dma_cb IDEState 0x561808b0f118; sector_num=1 n=259 cmd=DMA WRITE
ide_dma_cb_next IDEState 0x561808b0f118;
So far, pretty normal. IDE calls the HBA's DMA start, but the HBA doesn't have DMA enabled, so it stalls. Later, when we turn on DMA, the HBA engages the DMA callback and sets up the first transfer. This sets s->bus->dma->aiocb.
Then, we try to cancel DMA:
ide_cancel_dma_sync IDEState 0x561808b0f118; dma_sync_ remaining draining all remaining requests 1595891049. 469050: dma_blk_ cb dbs=0x55baededdc50 ret=0 1595891049. 469054: dma_map_ wait dbs=0x55baededdc50 src/qemu/ hw/ide/ core.c: 732: void ide_cancel_ dma_sync( IDEState *): Assertion `s->bus->dma->aiocb == NULL' failed.
ide_cancel_
1343877@
1343877@
qemu-system-i386: /home/jsnow/
We still have a DMA callback out, so we try to synchronously cancel it; but the blk_drain doesn't appear to be effective!
We apparently wind up here:
if (dbs->iov.size == 0) {
trace_ dma_map_ wait(dbs) ; new(dbs- >ctx, reschedule_dma, dbs);
cpu_register_ map_client( dbs->bh) ;
dbs->bh = aio_bh_
return;
}
... The DMA simply re-schedules itself (?) when iov.size is zero. unfortunately for us, that means that the original point of scheduling the drain doesn't work, because the DMA never returns all the way to the IDE device emulation code.