Re: [RFC PATCH 2/3] hw/sd/sdhci: Prohibit DMA accesses to devices

2021-12-15 Thread Jason Wang
On Thu, Dec 16, 2021 at 4:57 AM Philippe Mathieu-Daudé
 wrote:
>
> From: Philippe Mathieu-Daudé 
>
> The issue reported by OSS-Fuzz produces the following backtrace:
>
>   ==447470==ERROR: AddressSanitizer: heap-buffer-overflow
>   READ of size 1 at 0x6152a080 thread T0
>   #0 0x71766d47 in sdhci_read_dataport hw/sd/sdhci.c:474:18
>   #1 0x7175f139 in sdhci_read hw/sd/sdhci.c:1022:19
>   #2 0x721b937b in memory_region_read_accessor softmmu/memory.c:440:11
>   #3 0x72171e51 in access_with_adjusted_size softmmu/memory.c:554:18
>   #4 0x7216f47c in memory_region_dispatch_read1 softmmu/memory.c:1424:16
>   #5 0x7216ebb9 in memory_region_dispatch_read softmmu/memory.c:1452:9
>   #6 0x7212db5d in flatview_read_continue softmmu/physmem.c:2879:23
>   #7 0x7212f958 in flatview_read softmmu/physmem.c:2921:12
>   #8 0x7212f418 in address_space_read_full softmmu/physmem.c:2934:18
>   #9 0x721305a9 in address_space_rw softmmu/physmem.c:2962:16
>   #10 0x7175a392 in dma_memory_rw_relaxed include/sysemu/dma.h:89:12
>   #11 0x7175a0ea in dma_memory_rw include/sysemu/dma.h:132:12
>   #12 0x71759684 in dma_memory_read include/sysemu/dma.h:152:12
>   #13 0x7175518c in sdhci_do_adma hw/sd/sdhci.c:823:27
>   #14 0x7174bf69 in sdhci_data_transfer hw/sd/sdhci.c:935:13
>   #15 0x7176aaa7 in sdhci_send_command hw/sd/sdhci.c:376:9
>   #16 0x717629ee in sdhci_write hw/sd/sdhci.c:1212:9
>   #17 0x72172513 in memory_region_write_accessor softmmu/memory.c:492:5
>   #18 0x72171e51 in access_with_adjusted_size softmmu/memory.c:554:18
>   #19 0x72170766 in memory_region_dispatch_write softmmu/memory.c:1504:16
>   #20 0x721419ee in flatview_write_continue softmmu/physmem.c:2812:23
>   #21 0x721301eb in flatview_write softmmu/physmem.c:2854:12
>   #22 0x7212fca8 in address_space_write softmmu/physmem.c:2950:18
>   #23 0x721d9a53 in qtest_process_command softmmu/qtest.c:727:9
>
> A DMA descriptor is previously filled in RAM. An I/O access to the
> device (frames #22 to #16) start the DMA engine (frame #13). The
> engine fetch the descriptor and execute the request, which itself
> accesses the SDHCI I/O registers (frame #1 and #0), triggering a
> re-entrancy issue.
>
> Fix by prohibit transactions from the DMA to devices. The DMA engine
> is thus restricted to memories.
>
> Reported-by: OSS-Fuzz (Issue 36391)
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/451
> Signed-off-by: Philippe Mathieu-Daudé 
> ---
>  hw/sd/sdhci.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
> index fe2f21f0c37..0e5e988927e 100644
> --- a/hw/sd/sdhci.c
> +++ b/hw/sd/sdhci.c
> @@ -741,6 +741,7 @@ static void sdhci_do_adma(SDHCIState *s)
>  {
>  unsigned int begin, length;
>  const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
> +const MemTxAttrs attrs = { .memory = true };
>  ADMADescr dscr = {};
>  MemTxResult res;
>  int i;
> @@ -794,7 +795,7 @@ static void sdhci_do_adma(SDHCIState *s)
>  res = dma_memory_write(s->dma_as, dscr.addr,
> >fifo_buffer[begin],
> s->data_count - begin,
> -   MEMTXATTRS_UNSPECIFIED);
> +   attrs);
>  if (res != MEMTX_OK) {
>  break;
>  }
> @@ -823,7 +824,7 @@ static void sdhci_do_adma(SDHCIState *s)
>  res = dma_memory_read(s->dma_as, dscr.addr,
>>fifo_buffer[begin],
>s->data_count - begin,
> -  MEMTXATTRS_UNSPECIFIED);
> +  attrs);
>  if (res != MEMTX_OK) {
>  break;
>  }

I wonder how we can fix this for other devices, as this seems to be a
known issue for many years. We've received many reports from the
networking side.

It looks like this patch simply forbids p2p which is probably not the
case for other devices.

I remember there's ideas like using bh from Paolo or detecting
reentrancy in the memory core, both of them seems more general than
this?

Thanks

> --
> 2.33.1
>




[RFC PATCH 3/3] tests/qtest/fuzz-sdcard-test: Add reproducer for OSS-Fuzz (Issue 29225)

2021-12-15 Thread Philippe Mathieu-Daudé
Include the qtest reproducer provided by Alexander Bulekov
in https://gitlab.com/qemu-project/qemu/-/issues/451. Without
the previous commit, we get:

  $ make check-qtest-i386
  ...
  Running test qtest-i386/fuzz-sdcard-test
  ==447470==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x6152a080 at pc 0x564c71766d48 bp 0x7ffc126c62b0 sp 0x7ffc126c62a8
  READ of size 1 at 0x6152a080 thread T0
  #0 0x564c71766d47 in sdhci_read_dataport hw/sd/sdhci.c:474:18
  #1 0x564c7175f139 in sdhci_read hw/sd/sdhci.c:1022:19
  #2 0x564c721b937b in memory_region_read_accessor softmmu/memory.c:440:11
  #3 0x564c72171e51 in access_with_adjusted_size softmmu/memory.c:554:18
  #4 0x564c7216f47c in memory_region_dispatch_read1 softmmu/memory.c:1424:16
  #5 0x564c7216ebb9 in memory_region_dispatch_read softmmu/memory.c:1452:9
  #6 0x564c7212db5d in flatview_read_continue softmmu/physmem.c:2879:23
  #7 0x564c7212f958 in flatview_read softmmu/physmem.c:2921:12
  #8 0x564c7212f418 in address_space_read_full softmmu/physmem.c:2934:18
  #9 0x564c721305a9 in address_space_rw softmmu/physmem.c:2962:16
  #10 0x564c7175a392 in dma_memory_rw_relaxed include/sysemu/dma.h:89:12
  #11 0x564c7175a0ea in dma_memory_rw include/sysemu/dma.h:132:12
  #12 0x564c71759684 in dma_memory_read include/sysemu/dma.h:152:12
  #13 0x564c7175518c in sdhci_do_adma hw/sd/sdhci.c:823:27
  #14 0x564c7174bf69 in sdhci_data_transfer hw/sd/sdhci.c:935:13
  #15 0x564c7176aaa7 in sdhci_send_command hw/sd/sdhci.c:376:9
  #16 0x564c717629ee in sdhci_write hw/sd/sdhci.c:1212:9
  #17 0x564c72172513 in memory_region_write_accessor softmmu/memory.c:492:5
  #18 0x564c72171e51 in access_with_adjusted_size softmmu/memory.c:554:18
  #19 0x564c72170766 in memory_region_dispatch_write 
softmmu/memory.c:1504:16
  #20 0x564c721419ee in flatview_write_continue softmmu/physmem.c:2812:23
  #21 0x564c721301eb in flatview_write softmmu/physmem.c:2854:12
  #22 0x564c7212fca8 in address_space_write softmmu/physmem.c:2950:18
  #23 0x564c721d9a53 in qtest_process_command softmmu/qtest.c:727:9

  0x6152a080 is located 0 bytes to the right of 512-byte region 
[0x61529e80,0x6152a080)
  allocated by thread T0 here:
  #0 0x564c708e1737 in __interceptor_calloc (qemu-system-i386+0x1e6a737)
  #1 0x7ff05567b5e0 in g_malloc0 (/lib64/libglib-2.0.so.0+0x5a5e0)
  #2 0x564c71774adb in sdhci_pci_realize hw/sd/sdhci-pci.c:36:5

  SUMMARY: AddressSanitizer: heap-buffer-overflow hw/sd/sdhci.c:474:18 in 
sdhci_read_dataport
  Shadow bytes around the buggy address:
0x0c2a7fffd3c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a7fffd3d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a7fffd3e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a7fffd3f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a7fffd400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  =>0x0c2a7fffd410:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a7fffd420: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c2a7fffd430: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c2a7fffd440: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c2a7fffd450: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c2a7fffd460: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable:   00
Heap left redzone:   fa
Freed heap region:   fd
  ==447470==ABORTING
  Broken pipe
  ERROR qtest-i386/fuzz-sdcard-test - too few tests run (expected 3, got 2)

Signed-off-by: Philippe Mathieu-Daudé 
---
 tests/qtest/fuzz-sdcard-test.c | 76 ++
 1 file changed, 76 insertions(+)

diff --git a/tests/qtest/fuzz-sdcard-test.c b/tests/qtest/fuzz-sdcard-test.c
index ae14305344a..6dfe26e983c 100644
--- a/tests/qtest/fuzz-sdcard-test.c
+++ b/tests/qtest/fuzz-sdcard-test.c
@@ -87,6 +87,81 @@ static void oss_fuzz_36217(void)
 qtest_quit(s);
 }
 
+/*
+ * https://gitlab.com/qemu-project/qemu/-/issues/451
+ * Used to trigger a heap buffer overflow.
+ */
+static void oss_fuzz_36391(void)
+{
+QTestState *s;
+
+s = qtest_init(" -display none -m 4G -nodefaults -nographic"
+   " -device sdhci-pci,sd-spec-version=3"
+   " -device sd-card,drive=drv"
+   " -drive 
if=none,index=0,file=null-co://,format=raw,id=drv");
+qtest_outl(s, 0xcf8, 0x80001010);
+qtest_outl(s, 0xcfc, 0xe000);
+qtest_outl(s, 0xcf8, 0x80001004);
+qtest_outw(s, 0xcfc, 0x7);
+qtest_bufwrite(s, 0xe005, "\x73", 0x1);
+qtest_bufwrite(s, 0xe028, "\x55", 0x1);
+qtest_bufwrite(s, 0xe02c, "\x55", 0x1);
+qtest_bufwrite(s, 0x0, "\x65", 0x1);
+qtest_bufwrite(s, 0x7, "\x69", 0x1);
+qtest_bufwrite(s, 0x8, "\x65", 0x1);
+qtest_bufwrite(s, 0xf, "\x69", 0x1);
+qtest_bufwrite(s, 

[RFC PATCH 2/3] hw/sd/sdhci: Prohibit DMA accesses to devices

2021-12-15 Thread Philippe Mathieu-Daudé
From: Philippe Mathieu-Daudé 

The issue reported by OSS-Fuzz produces the following backtrace:

  ==447470==ERROR: AddressSanitizer: heap-buffer-overflow
  READ of size 1 at 0x6152a080 thread T0
  #0 0x71766d47 in sdhci_read_dataport hw/sd/sdhci.c:474:18
  #1 0x7175f139 in sdhci_read hw/sd/sdhci.c:1022:19
  #2 0x721b937b in memory_region_read_accessor softmmu/memory.c:440:11
  #3 0x72171e51 in access_with_adjusted_size softmmu/memory.c:554:18
  #4 0x7216f47c in memory_region_dispatch_read1 softmmu/memory.c:1424:16
  #5 0x7216ebb9 in memory_region_dispatch_read softmmu/memory.c:1452:9
  #6 0x7212db5d in flatview_read_continue softmmu/physmem.c:2879:23
  #7 0x7212f958 in flatview_read softmmu/physmem.c:2921:12
  #8 0x7212f418 in address_space_read_full softmmu/physmem.c:2934:18
  #9 0x721305a9 in address_space_rw softmmu/physmem.c:2962:16
  #10 0x7175a392 in dma_memory_rw_relaxed include/sysemu/dma.h:89:12
  #11 0x7175a0ea in dma_memory_rw include/sysemu/dma.h:132:12
  #12 0x71759684 in dma_memory_read include/sysemu/dma.h:152:12
  #13 0x7175518c in sdhci_do_adma hw/sd/sdhci.c:823:27
  #14 0x7174bf69 in sdhci_data_transfer hw/sd/sdhci.c:935:13
  #15 0x7176aaa7 in sdhci_send_command hw/sd/sdhci.c:376:9
  #16 0x717629ee in sdhci_write hw/sd/sdhci.c:1212:9
  #17 0x72172513 in memory_region_write_accessor softmmu/memory.c:492:5
  #18 0x72171e51 in access_with_adjusted_size softmmu/memory.c:554:18
  #19 0x72170766 in memory_region_dispatch_write softmmu/memory.c:1504:16
  #20 0x721419ee in flatview_write_continue softmmu/physmem.c:2812:23
  #21 0x721301eb in flatview_write softmmu/physmem.c:2854:12
  #22 0x7212fca8 in address_space_write softmmu/physmem.c:2950:18
  #23 0x721d9a53 in qtest_process_command softmmu/qtest.c:727:9

A DMA descriptor is previously filled in RAM. An I/O access to the
device (frames #22 to #16) start the DMA engine (frame #13). The
engine fetch the descriptor and execute the request, which itself
accesses the SDHCI I/O registers (frame #1 and #0), triggering a
re-entrancy issue.

Fix by prohibit transactions from the DMA to devices. The DMA engine
is thus restricted to memories.

Reported-by: OSS-Fuzz (Issue 36391)
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/451
Signed-off-by: Philippe Mathieu-Daudé 
---
 hw/sd/sdhci.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index fe2f21f0c37..0e5e988927e 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -741,6 +741,7 @@ static void sdhci_do_adma(SDHCIState *s)
 {
 unsigned int begin, length;
 const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
+const MemTxAttrs attrs = { .memory = true };
 ADMADescr dscr = {};
 MemTxResult res;
 int i;
@@ -794,7 +795,7 @@ static void sdhci_do_adma(SDHCIState *s)
 res = dma_memory_write(s->dma_as, dscr.addr,
>fifo_buffer[begin],
s->data_count - begin,
-   MEMTXATTRS_UNSPECIFIED);
+   attrs);
 if (res != MEMTX_OK) {
 break;
 }
@@ -823,7 +824,7 @@ static void sdhci_do_adma(SDHCIState *s)
 res = dma_memory_read(s->dma_as, dscr.addr,
   >fifo_buffer[begin],
   s->data_count - begin,
-  MEMTXATTRS_UNSPECIFIED);
+  attrs);
 if (res != MEMTX_OK) {
 break;
 }
-- 
2.33.1




[RFC PATCH 1/3] hw/sd/sdhci: Honor failed DMA transactions

2021-12-15 Thread Philippe Mathieu-Daudé
From: Philippe Mathieu-Daudé 

DMA transactions might fail. The DMA API returns a MemTxResult,
indicating such failures. Do not ignore it. On failure, raise
the ADMA error flag and eventually triggering an IRQ (see spec
chapter 1.13.5: "ADMA2 States").

Signed-off-by: Philippe Mathieu-Daudé 
---
 hw/sd/sdhci.c | 34 +-
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index e0bbc903446..fe2f21f0c37 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -742,6 +742,7 @@ static void sdhci_do_adma(SDHCIState *s)
 unsigned int begin, length;
 const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
 ADMADescr dscr = {};
+MemTxResult res;
 int i;
 
 if (s->trnmod & SDHC_TRNS_BLK_CNT_EN && !s->blkcnt) {
@@ -790,10 +791,13 @@ static void sdhci_do_adma(SDHCIState *s)
 s->data_count = block_size;
 length -= block_size - begin;
 }
-dma_memory_write(s->dma_as, dscr.addr,
- >fifo_buffer[begin],
- s->data_count - begin,
- MEMTXATTRS_UNSPECIFIED);
+res = dma_memory_write(s->dma_as, dscr.addr,
+   >fifo_buffer[begin],
+   s->data_count - begin,
+   MEMTXATTRS_UNSPECIFIED);
+if (res != MEMTX_OK) {
+break;
+}
 dscr.addr += s->data_count - begin;
 if (s->data_count == block_size) {
 s->data_count = 0;
@@ -816,10 +820,13 @@ static void sdhci_do_adma(SDHCIState *s)
 s->data_count = block_size;
 length -= block_size - begin;
 }
-dma_memory_read(s->dma_as, dscr.addr,
->fifo_buffer[begin],
-s->data_count - begin,
-MEMTXATTRS_UNSPECIFIED);
+res = dma_memory_read(s->dma_as, dscr.addr,
+  >fifo_buffer[begin],
+  s->data_count - begin,
+  MEMTXATTRS_UNSPECIFIED);
+if (res != MEMTX_OK) {
+break;
+}
 dscr.addr += s->data_count - begin;
 if (s->data_count == block_size) {
 sdbus_write_data(>sdbus, s->fifo_buffer, 
block_size);
@@ -833,7 +840,16 @@ static void sdhci_do_adma(SDHCIState *s)
 }
 }
 }
-s->admasysaddr += dscr.incr;
+if (res != MEMTX_OK) {
+if (s->errintstsen & SDHC_EISEN_ADMAERR) {
+trace_sdhci_error("Set ADMA error flag");
+s->errintsts |= SDHC_EIS_ADMAERR;
+s->norintsts |= SDHC_NIS_ERR;
+}
+sdhci_update_irq(s);
+} else {
+s->admasysaddr += dscr.incr;
+}
 break;
 case SDHC_ADMA_ATTR_ACT_LINK:   /* link to next descriptor table */
 s->admasysaddr = dscr.addr;
-- 
2.33.1




Re: [PATCH 0/3] hw/sd/sdhci: Fix DMA re-entrancy issue

2021-12-15 Thread Philippe Mathieu-Daudé
On 12/15/21 21:55, Philippe Mathieu-Daudé wrote:
> Hi,
> 
> This series is an attempt to fix the DMA re-entrancy problem
> on the SDHCI device. OSS-Fuzz found it and Alexander generated
> a helpful reproducer.
> 
> By setting the MemTxAttrs::memory bit before doing DMA transactions,
> the flatview API will return MEMTX_BUS_ERROR if the transaction
> targets a non-memory (a device), which is usually how DMA-reentrancy
> bugs are exploited.
> 
> On real hardware, the checks are on the interconnect bus, not in
> the SDHCI block. However QEMU blocks aren't modelled that way.
> Using the flatview API seems (to me) the simplest and closer
> to hardware, it is a generic API and we can use it to trace
> bus transactions on all blocks.
> 
> Note this series is simply one example to fix the generic
> issues. The important changes are in the previous series:
> https://lore.kernel.org/qemu-devel/20211215182421.418374-1-phi...@redhat.com/
> Based-on: <20211215182421.418374-1-phi...@redhat.com>
> "physmem: Have flatview API check bus permission from MemTxAttrs"

Please disregard this cover, I forgot to post as RFC...




[RFC PATCH 0/3] hw/sd/sdhci: Fix DMA re-entrancy issue

2021-12-15 Thread Philippe Mathieu-Daudé
Hi,

This series is an attempt to fix the DMA re-entrancy problem
on the SDHCI device. OSS-Fuzz found it and Alexander generated
a helpful reproducer.

By setting the MemTxAttrs::memory bit before doing DMA transactions,
the flatview API will return MEMTX_BUS_ERROR if the transaction
targets a non-memory (a device), which is usually how DMA-reentrancy
bugs are exploited.

On real hardware, the checks are on the interconnect bus, not in
the SDHCI block. However QEMU blocks aren't modelled that way.
Using the flatview API seems (to me) the simplest and closer
to hardware, it is a generic API and we can use it to trace
bus transactions on all blocks.

Note this series is simply one example to fix the generic
issues. The important changes are in the previous series:
https://lore.kernel.org/qemu-devel/20211215182421.418374-1-phi...@redhat.com/
Based-on: <20211215182421.418374-1-phi...@redhat.com>
"physmem: Have flatview API check bus permission from MemTxAttrs"

Cc: Mauro Matteo Cascella 
Cc: Qiuhao Li 
Cc: Peter Xu 
Cc: Jason Wang 
Cc: David Hildenbrand 
Cc: Gerd Hoffmann 
Cc: Peter Maydell 
Cc: Li Qiang 
Cc: Thomas Huth 
Cc: Laurent Vivier 
Cc: Bandan Das 
Cc: Edgar E. Iglesias 
Cc: Darren Kenny 
Cc: Bin Meng 
Cc: Paolo Bonzini 
Cc: Alexander Bulekov 
Cc: Stefan Hajnoczi 

Philippe Mathieu-Daudé (3):
  hw/sd/sdhci: Honor failed DMA transactions
  hw/sd/sdhci: Prohibit DMA accesses to devices
  tests/qtest/fuzz-sdcard-test: Add reproducer for OSS-Fuzz (Issue
29225)

 hw/sd/sdhci.c  | 35 
 tests/qtest/fuzz-sdcard-test.c | 76 ++
 2 files changed, 102 insertions(+), 9 deletions(-)

-- 
2.33.1





[PATCH 0/3] hw/sd/sdhci: Fix DMA re-entrancy issue

2021-12-15 Thread Philippe Mathieu-Daudé
Hi,

This series is an attempt to fix the DMA re-entrancy problem
on the SDHCI device. OSS-Fuzz found it and Alexander generated
a helpful reproducer.

By setting the MemTxAttrs::memory bit before doing DMA transactions,
the flatview API will return MEMTX_BUS_ERROR if the transaction
targets a non-memory (a device), which is usually how DMA-reentrancy
bugs are exploited.

On real hardware, the checks are on the interconnect bus, not in
the SDHCI block. However QEMU blocks aren't modelled that way.
Using the flatview API seems (to me) the simplest and closer
to hardware, it is a generic API and we can use it to trace
bus transactions on all blocks.

Note this series is simply one example to fix the generic
issues. The important changes are in the previous series:
https://lore.kernel.org/qemu-devel/20211215182421.418374-1-phi...@redhat.com/
Based-on: <20211215182421.418374-1-phi...@redhat.com>
"physmem: Have flatview API check bus permission from MemTxAttrs"

Cc: Mauro Matteo Cascella 
Cc: Qiuhao Li 
Cc: Peter Xu 
Cc: Jason Wang 
Cc: David Hildenbrand 
Cc: Gerd Hoffmann 
Cc: Peter Maydell 
Cc: Li Qiang 
Cc: Thomas Huth 
Cc: Laurent Vivier 
Cc: Bandan Das 
Cc: Edgar E. Iglesias 
Cc: Darren Kenny 
Cc: Bin Meng 
Cc: Paolo Bonzini 
Cc: Alexander Bulekov 
Cc: Stefan Hajnoczi 

Philippe Mathieu-Daudé (3):
  hw/sd/sdhci: Honor failed DMA transactions
  hw/sd/sdhci: Prohibit DMA accesses to devices
  tests/qtest/fuzz-sdcard-test: Add reproducer for OSS-Fuzz (Issue
29225)

 hw/sd/sdhci.c  | 35 
 tests/qtest/fuzz-sdcard-test.c | 76 ++
 2 files changed, 102 insertions(+), 9 deletions(-)

-- 
2.33.1





[PATCH v2 23/25] python: remove the old QMP package

2021-12-15 Thread John Snow
Thank you for your service!

Signed-off-by: John Snow 
---
 python/PACKAGE.rst  |   4 +-
 python/README.rst   |   2 +-
 python/qemu/qmp/README.rst  |   9 -
 python/qemu/qmp/__init__.py | 396 
 python/qemu/qmp/py.typed|   0
 python/setup.cfg|   3 +-
 6 files changed, 4 insertions(+), 410 deletions(-)
 delete mode 100644 python/qemu/qmp/README.rst
 delete mode 100644 python/qemu/qmp/__init__.py
 delete mode 100644 python/qemu/qmp/py.typed

diff --git a/python/PACKAGE.rst b/python/PACKAGE.rst
index b0b86cc4c3..ddfa9ba3f5 100644
--- a/python/PACKAGE.rst
+++ b/python/PACKAGE.rst
@@ -8,11 +8,11 @@ to change at any time.
 Usage
 -
 
-The ``qemu.qmp`` subpackage provides a library for communicating with
+The ``qemu.aqmp`` subpackage provides a library for communicating with
 QMP servers. The ``qemu.machine`` subpackage offers rudimentary
 facilities for launching and managing QEMU processes. Refer to each
 package's documentation
-(``>>> help(qemu.qmp)``, ``>>> help(qemu.machine)``)
+(``>>> help(qemu.aqmp)``, ``>>> help(qemu.machine)``)
 for more information.
 
 Contributing
diff --git a/python/README.rst b/python/README.rst
index fcf74f69ea..eb5213337d 100644
--- a/python/README.rst
+++ b/python/README.rst
@@ -3,7 +3,7 @@ QEMU Python Tooling
 
 This directory houses Python tooling used by the QEMU project to build,
 configure, and test QEMU. It is organized by namespace (``qemu``), and
-then by package (e.g. ``qemu/machine``, ``qemu/qmp``, etc).
+then by package (e.g. ``qemu/machine``, ``qemu/aqmp``, etc).
 
 ``setup.py`` is used by ``pip`` to install this tooling to the current
 environment. ``setup.cfg`` provides the packaging configuration used by
diff --git a/python/qemu/qmp/README.rst b/python/qemu/qmp/README.rst
deleted file mode 100644
index 5bfb82535f..00
--- a/python/qemu/qmp/README.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-qemu.qmp package
-
-
-This package provides a library used for connecting to and communicating
-with QMP servers. It is used extensively by iotests, vm tests,
-avocado tests, and other utilities in the ./scripts directory. It is
-not a fully-fledged SDK and is subject to change at any time.
-
-See the documentation in ``__init__.py`` for more information.
diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
deleted file mode 100644
index 4e08641154..00
--- a/python/qemu/qmp/__init__.py
+++ /dev/null
@@ -1,396 +0,0 @@
-"""
-QEMU Monitor Protocol (QMP) development library & tooling.
-
-This package provides a fairly low-level class for communicating to QMP
-protocol servers, as implemented by QEMU, the QEMU Guest Agent, and the
-QEMU Storage Daemon. This library is not intended for production use.
-
-`QEMUMonitorProtocol` is the primary class of interest, and all errors
-raised derive from `QMPError`.
-"""
-
-# Copyright (C) 2009, 2010 Red Hat Inc.
-#
-# Authors:
-#  Luiz Capitulino 
-#
-# This work is licensed under the terms of the GNU GPL, version 2.  See
-# the COPYING file in the top-level directory.
-
-import errno
-import json
-import logging
-import socket
-import struct
-from types import TracebackType
-from typing import (
-Any,
-Dict,
-List,
-Optional,
-TextIO,
-Tuple,
-Type,
-TypeVar,
-Union,
-cast,
-)
-
-
-#: QMPMessage is an entire QMP message of any kind.
-QMPMessage = Dict[str, Any]
-
-#: QMPReturnValue is the 'return' value of a command.
-QMPReturnValue = object
-
-#: QMPObject is any object in a QMP message.
-QMPObject = Dict[str, object]
-
-# QMPMessage can be outgoing commands or incoming events/returns.
-# QMPReturnValue is usually a dict/json object, but due to QAPI's
-# 'returns-whitelist', it can actually be anything.
-#
-# {'return': {}} is a QMPMessage,
-# {} is the QMPReturnValue.
-
-
-InternetAddrT = Tuple[str, int]
-UnixAddrT = str
-SocketAddrT = Union[InternetAddrT, UnixAddrT]
-
-
-class QMPError(Exception):
-"""
-QMP base exception
-"""
-
-
-class QMPConnectError(QMPError):
-"""
-QMP connection exception
-"""
-
-
-class QMPCapabilitiesError(QMPError):
-"""
-QMP negotiate capabilities exception
-"""
-
-
-class QMPTimeoutError(QMPError):
-"""
-QMP timeout exception
-"""
-
-
-class QMPProtocolError(QMPError):
-"""
-QMP protocol error; unexpected response
-"""
-
-
-class QMPResponseError(QMPError):
-"""
-Represents erroneous QMP monitor reply
-"""
-def __init__(self, reply: QMPMessage):
-try:
-desc = reply['error']['desc']
-except KeyError:
-desc = reply
-super().__init__(desc)
-self.reply = reply
-
-
-class QEMUMonitorProtocol:
-"""
-Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
-allow to handle commands and events.
-"""
-
-#: Logger object for debugging messages
-logger = logging.getLogger('QMP')
-
-def __init__(self, 

[PATCH v2 20/25] python/aqmp: take QMPBadPortError and parse_address from qemu.qmp

2021-12-15 Thread John Snow
Shift these definitions over from the qmp package to the async qmp
package.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/aqmp_tui.py |  2 +-
 python/qemu/aqmp/legacy.py   | 30 ++
 python/qemu/qmp/__init__.py  | 26 --
 3 files changed, 27 insertions(+), 31 deletions(-)

diff --git a/python/qemu/aqmp/aqmp_tui.py b/python/qemu/aqmp/aqmp_tui.py
index a2929f771c..184a3e4690 100644
--- a/python/qemu/aqmp/aqmp_tui.py
+++ b/python/qemu/aqmp/aqmp_tui.py
@@ -35,8 +35,8 @@
 import urwid
 import urwid_readline
 
-from ..qmp import QEMUMonitorProtocol, QMPBadPortError
 from .error import ProtocolError
+from .legacy import QEMUMonitorProtocol, QMPBadPortError
 from .message import DeserializationError, Message, UnexpectedTypeError
 from .protocol import ConnectError, Runstate
 from .qmp_client import ExecInterruptedError, QMPClient
diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
index 0890f95b16..76b09671cc 100644
--- a/python/qemu/aqmp/legacy.py
+++ b/python/qemu/aqmp/legacy.py
@@ -22,9 +22,6 @@
 from .qmp_client import QMPClient
 
 
-# (Temporarily) Re-export QMPBadPortError
-QMPBadPortError = qemu.qmp.QMPBadPortError
-
 #: QMPMessage is an entire QMP message of any kind.
 QMPMessage = Dict[str, Any]
 
@@ -45,6 +42,12 @@
 # pylint: disable=missing-docstring
 
 
+class QMPBadPortError(QMPError):
+"""
+Unable to parse socket address: Port was non-numerical.
+"""
+
+
 class QEMUMonitorProtocol(qemu.qmp.QEMUMonitorProtocol):
 def __init__(self, address: SocketAddrT,
  server: bool = False,
@@ -72,7 +75,26 @@ def _get_greeting(self) -> Optional[QMPMessage]:
 return None
 
 # __enter__ and __exit__ need no changes
-# parse_address needs no changes
+
+@classmethod
+def parse_address(cls, address: str) -> SocketAddrT:
+"""
+Parse a string into a QMP address.
+
+Figure out if the argument is in the port:host form.
+If it's not, it's probably a file path.
+"""
+components = address.split(':')
+if len(components) == 2:
+try:
+port = int(components[1])
+except ValueError:
+msg = f"Bad port: '{components[1]}' in '{address}'."
+raise QMPBadPortError(msg) from None
+return (components[0], port)
+
+# Treat as filepath.
+return address
 
 def connect(self, negotiate: bool = True) -> Optional[QMPMessage]:
 self._aqmp.await_greeting = negotiate
diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
index 358c0971d0..4e08641154 100644
--- a/python/qemu/qmp/__init__.py
+++ b/python/qemu/qmp/__init__.py
@@ -102,12 +102,6 @@ def __init__(self, reply: QMPMessage):
 self.reply = reply
 
 
-class QMPBadPortError(QMPError):
-"""
-Unable to parse socket address: Port was non-numerical.
-"""
-
-
 class QEMUMonitorProtocol:
 """
 Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
@@ -237,26 +231,6 @@ def __exit__(self,
 # Implement context manager exit function.
 self.close()
 
-@classmethod
-def parse_address(cls, address: str) -> SocketAddrT:
-"""
-Parse a string into a QMP address.
-
-Figure out if the argument is in the port:host form.
-If it's not, it's probably a file path.
-"""
-components = address.split(':')
-if len(components) == 2:
-try:
-port = int(components[1])
-except ValueError:
-msg = f"Bad port: '{components[1]}' in '{address}'."
-raise QMPBadPortError(msg) from None
-return (components[0], port)
-
-# Treat as filepath.
-return address
-
 def connect(self, negotiate: bool = True) -> Optional[QMPMessage]:
 """
 Connect to the QMP Monitor and perform capabilities negotiation.
-- 
2.31.1




[PATCH v2 21/25] python/aqmp: fully separate from qmp.QEMUMonitorProtocol

2021-12-15 Thread John Snow
After this patch, qemu.aqmp.legacy.QEMUMonitorProtocol no longer
inherits from qemu.qmp.QEMUMonitorProtocol. To do this, several
inherited methods need to be explicitly re-defined.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/legacy.py | 38 --
 1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
index 76b09671cc..8f38e7d912 100644
--- a/python/qemu/aqmp/legacy.py
+++ b/python/qemu/aqmp/legacy.py
@@ -5,18 +5,18 @@
 """
 
 import asyncio
+from types import TracebackType
 from typing import (
 Any,
 Awaitable,
 Dict,
 List,
 Optional,
+Type,
 TypeVar,
 Union,
 )
 
-import qemu.qmp
-
 from .error import QMPError
 from .protocol import Runstate, SocketAddrT
 from .qmp_client import QMPClient
@@ -48,9 +48,9 @@ class QMPBadPortError(QMPError):
 """
 
 
-class QEMUMonitorProtocol(qemu.qmp.QEMUMonitorProtocol):
+class QEMUMonitorProtocol:
 def __init__(self, address: SocketAddrT,
- server: bool = False,
+ server: bool = False,  # pylint: disable=unused-argument
  nickname: Optional[str] = None):
 
 # pylint: disable=super-init-not-called
@@ -74,7 +74,18 @@ def _get_greeting(self) -> Optional[QMPMessage]:
 return self._aqmp.greeting._asdict()
 return None
 
-# __enter__ and __exit__ need no changes
+def __enter__(self: _T) -> _T:
+# Implement context manager enter function.
+return self
+
+def __exit__(self,
+ # pylint: disable=duplicate-code
+ # see https://github.com/PyCQA/pylint/issues/3619
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType]) -> None:
+# Implement context manager exit function.
+self.close()
 
 @classmethod
 def parse_address(cls, address: str) -> SocketAddrT:
@@ -131,7 +142,22 @@ def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
 )
 )
 
-# Default impl of cmd() delegates to cmd_obj
+def cmd(self, name: str,
+args: Optional[Dict[str, object]] = None,
+cmd_id: Optional[object] = None) -> QMPMessage:
+"""
+Build a QMP command and send it to the QMP Monitor.
+
+@param name: command name (string)
+@param args: command arguments (dict)
+@param cmd_id: command id (dict, list, string or int)
+"""
+qmp_cmd: QMPMessage = {'execute': name}
+if args:
+qmp_cmd['arguments'] = args
+if cmd_id:
+qmp_cmd['id'] = cmd_id
+return self.cmd_obj(qmp_cmd)
 
 def command(self, cmd: str, **kwds: object) -> QMPReturnValue:
 return self._sync(
-- 
2.31.1




[PATCH v2 25/25] python: rename qemu.aqmp to qemu.qmp

2021-12-15 Thread John Snow
Now that we are fully switched over to the new QMP library, move it back
over the old namespace. This is being done primarily so that we may
upload this package simply as "qemu.qmp" without introducing confusion
over whether or not "aqmp" is a new protocol or not.

The trade-off is increased confusion inside the QEMU developer
tree. Sorry!

Signed-off-by: John Snow 
---
 python/PACKAGE.rst|  4 ++--
 python/README.rst |  4 ++--
 python/qemu/machine/machine.py|  4 ++--
 python/qemu/machine/qtest.py  |  2 +-
 python/qemu/{aqmp => qmp}/__init__.py |  6 +++---
 python/qemu/{aqmp => qmp}/aqmp_tui.py |  0
 python/qemu/{aqmp => qmp}/error.py|  0
 python/qemu/{aqmp => qmp}/events.py   |  2 +-
 python/qemu/{aqmp => qmp}/legacy.py   |  0
 python/qemu/{aqmp => qmp}/message.py  |  0
 python/qemu/{aqmp => qmp}/models.py   |  0
 python/qemu/{aqmp => qmp}/protocol.py |  4 ++--
 python/qemu/{aqmp => qmp}/py.typed|  0
 python/qemu/{aqmp => qmp}/qmp_client.py   | 16 
 python/qemu/{aqmp => qmp}/qmp_shell.py|  4 ++--
 python/qemu/{aqmp => qmp}/util.py |  0
 python/qemu/utils/qemu_ga_client.py   |  4 ++--
 python/qemu/utils/qom.py  |  2 +-
 python/qemu/utils/qom_common.py   |  4 ++--
 python/qemu/utils/qom_fuse.py |  2 +-
 python/setup.cfg  |  8 
 python/tests/protocol.py  | 14 +++---
 scripts/cpu-x86-uarch-abi.py  |  2 +-
 scripts/device-crash-test |  4 ++--
 scripts/qmp/qmp-shell |  2 +-
 scripts/render_block_graph.py |  4 ++--
 scripts/simplebench/bench_block_job.py|  2 +-
 tests/qemu-iotests/iotests.py |  2 +-
 tests/qemu-iotests/tests/mirror-top-perms |  6 +++---
 29 files changed, 51 insertions(+), 51 deletions(-)
 rename python/qemu/{aqmp => qmp}/__init__.py (87%)
 rename python/qemu/{aqmp => qmp}/aqmp_tui.py (100%)
 rename python/qemu/{aqmp => qmp}/error.py (100%)
 rename python/qemu/{aqmp => qmp}/events.py (99%)
 rename python/qemu/{aqmp => qmp}/legacy.py (100%)
 rename python/qemu/{aqmp => qmp}/message.py (100%)
 rename python/qemu/{aqmp => qmp}/models.py (100%)
 rename python/qemu/{aqmp => qmp}/protocol.py (99%)
 rename python/qemu/{aqmp => qmp}/py.typed (100%)
 rename python/qemu/{aqmp => qmp}/qmp_client.py (97%)
 rename python/qemu/{aqmp => qmp}/qmp_shell.py (99%)
 rename python/qemu/{aqmp => qmp}/util.py (100%)

diff --git a/python/PACKAGE.rst b/python/PACKAGE.rst
index ddfa9ba3f5..b0b86cc4c3 100644
--- a/python/PACKAGE.rst
+++ b/python/PACKAGE.rst
@@ -8,11 +8,11 @@ to change at any time.
 Usage
 -
 
-The ``qemu.aqmp`` subpackage provides a library for communicating with
+The ``qemu.qmp`` subpackage provides a library for communicating with
 QMP servers. The ``qemu.machine`` subpackage offers rudimentary
 facilities for launching and managing QEMU processes. Refer to each
 package's documentation
-(``>>> help(qemu.aqmp)``, ``>>> help(qemu.machine)``)
+(``>>> help(qemu.qmp)``, ``>>> help(qemu.machine)``)
 for more information.
 
 Contributing
diff --git a/python/README.rst b/python/README.rst
index eb5213337d..9c1fceaee7 100644
--- a/python/README.rst
+++ b/python/README.rst
@@ -3,7 +3,7 @@ QEMU Python Tooling
 
 This directory houses Python tooling used by the QEMU project to build,
 configure, and test QEMU. It is organized by namespace (``qemu``), and
-then by package (e.g. ``qemu/machine``, ``qemu/aqmp``, etc).
+then by package (e.g. ``qemu/machine``, ``qemu/qmp``, etc).
 
 ``setup.py`` is used by ``pip`` to install this tooling to the current
 environment. ``setup.cfg`` provides the packaging configuration used by
@@ -59,7 +59,7 @@ Package installation also normally provides executable 
console scripts,
 so that tools like ``qmp-shell`` are always available via $PATH. To
 invoke them without installation, you can invoke e.g.:
 
-``> PYTHONPATH=~/src/qemu/python python3 -m qemu.aqmp.qmp_shell``
+``> PYTHONPATH=~/src/qemu/python python3 -m qemu.qmp.qmp_shell``
 
 The mappings between console script name and python module path can be
 found in ``setup.cfg``.
diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py
index 21fb4a4f30..6e4bf6520c 100644
--- a/python/qemu/machine/machine.py
+++ b/python/qemu/machine/machine.py
@@ -40,8 +40,8 @@
 TypeVar,
 )
 
-from qemu.aqmp import SocketAddrT
-from qemu.aqmp.legacy import (
+from qemu.qmp import SocketAddrT
+from qemu.qmp.legacy import (
 QEMUMonitorProtocol,
 QMPMessage,
 QMPReturnValue,
diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py
index 817c8a5425..f29d5c1042 100644
--- a/python/qemu/machine/qtest.py
+++ b/python/qemu/machine/qtest.py
@@ -26,7 +26,7 @@
 TextIO,
 )
 
-from qemu.aqmp.protocol import SocketAddrT
+from qemu.qmp.protocol import SocketAddrT
 
 from .machine import 

[PATCH v2 22/25] python/aqmp: copy qmp docstrings to qemu.aqmp.legacy

2021-12-15 Thread John Snow
Copy the docstrings out of qemu.qmp, adjusting them as necessary to
more accurately reflect the current state of this class.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/legacy.py | 110 ++---
 1 file changed, 102 insertions(+), 8 deletions(-)

diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
index 8f38e7d912..6c250cd46a 100644
--- a/python/qemu/aqmp/legacy.py
+++ b/python/qemu/aqmp/legacy.py
@@ -1,9 +1,23 @@
 """
-Sync QMP Wrapper
+(Legacy) Sync QMP Wrapper
 
-This class pretends to be qemu.qmp.QEMUMonitorProtocol.
+This module provides the `QEMUMonitorProtocol` class, which is a
+synchronous wrapper around `QMPClient`.
+
+Its design closely resembles that of the original QEMUMonitorProtocol
+class, originally written by Luiz Capitulino.
 """
 
+# Copyright (C) 2009, 2010, 2021 Red Hat Inc.
+#
+# Authors:
+#  Luiz Capitulino 
+#  John Snow 
+#
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+
+
 import asyncio
 from types import TracebackType
 from typing import (
@@ -39,9 +53,6 @@
 # {} is the QMPReturnValue.
 
 
-# pylint: disable=missing-docstring
-
-
 class QMPBadPortError(QMPError):
 """
 Unable to parse socket address: Port was non-numerical.
@@ -49,6 +60,21 @@ class QMPBadPortError(QMPError):
 
 
 class QEMUMonitorProtocol:
+"""
+Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP)
+and then allow to handle commands and events.
+
+:param address:  QEMU address, can be either a unix socket path (string)
+ or a tuple in the form ( address, port ) for a TCP
+ connection
+:param server:   Deprecated, ignored. (See 'accept')
+:param nickname: Optional nickname used for logging.
+
+..note::
+No connection is established during `__init__`, this is done by
+the `connect()` or `accept()` methods.
+"""
+
 def __init__(self, address: SocketAddrT,
  server: bool = False,  # pylint: disable=unused-argument
  nickname: Optional[str] = None):
@@ -108,6 +134,12 @@ def parse_address(cls, address: str) -> SocketAddrT:
 return address
 
 def connect(self, negotiate: bool = True) -> Optional[QMPMessage]:
+"""
+Connect to the QMP Monitor and perform capabilities negotiation.
+
+:return: QMP greeting dict, or None if negotiate is false
+:raise ConnectError: on connection errors
+"""
 self._aqmp.await_greeting = negotiate
 self._aqmp.negotiate = negotiate
 
@@ -117,6 +149,16 @@ def connect(self, negotiate: bool = True) -> 
Optional[QMPMessage]:
 return self._get_greeting()
 
 def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage:
+"""
+Await connection from QMP Monitor and perform capabilities negotiation.
+
+:param timeout:
+timeout in seconds (nonnegative float number, or None).
+If None, there is no timeout, and this may block forever.
+
+:return: QMP greeting dict
+:raise ConnectError: on connection errors
+"""
 self._aqmp.await_greeting = True
 self._aqmp.negotiate = True
 
@@ -130,6 +172,12 @@ def accept(self, timeout: Optional[float] = 15.0) -> 
QMPMessage:
 return ret
 
 def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
+"""
+Send a QMP command to the QMP Monitor.
+
+:param qmp_cmd: QMP command to be sent as a Python dict
+:return: QMP response as a Python dict
+"""
 return dict(
 self._sync(
 # pylint: disable=protected-access
@@ -148,9 +196,9 @@ def cmd(self, name: str,
 """
 Build a QMP command and send it to the QMP Monitor.
 
-@param name: command name (string)
-@param args: command arguments (dict)
-@param cmd_id: command id (dict, list, string or int)
+:param name: command name (string)
+:param args: command arguments (dict)
+:param cmd_id: command id (dict, list, string or int)
 """
 qmp_cmd: QMPMessage = {'execute': name}
 if args:
@@ -160,6 +208,9 @@ def cmd(self, name: str,
 return self.cmd_obj(qmp_cmd)
 
 def command(self, cmd: str, **kwds: object) -> QMPReturnValue:
+"""
+Build and send a QMP command to the monitor, report errors if any
+"""
 return self._sync(
 self._aqmp.execute(cmd, kwds),
 self._timeout
@@ -167,6 +218,19 @@ def command(self, cmd: str, **kwds: object) -> 
QMPReturnValue:
 
 def pull_event(self,
wait: Union[bool, float] = False) -> Optional[QMPMessage]:
+"""
+Pulls a single event.
+
+:param wait:
+If False or 0, do not wait. Return None if no events ready.
+If True, wait forever until the next event.
+Otherwise, 

[PATCH v2 19/25] python: temporarily silence pylint duplicate-code warnings

2021-12-15 Thread John Snow
The next several commits copy some code from qemu.qmp to qemu.aqmp, then
delete qemu.qmp. In the interim, to prevent test failures, the duplicate
code detection needs to be silenced to prevent bisect problems with CI
testing.

Signed-off-by: John Snow 
---
 python/setup.cfg | 1 +
 1 file changed, 1 insertion(+)

diff --git a/python/setup.cfg b/python/setup.cfg
index 168a79c867..510df23698 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -115,6 +115,7 @@ ignore_missing_imports = True
 disable=consider-using-f-string,
 too-many-function-args,  # mypy handles this with less false positives.
 no-member,  # mypy also handles this better.
+duplicate-code,  # To be removed by the end of this patch series.
 
 [pylint.basic]
 # Good variable names which should always be accepted, separated by a comma.
-- 
2.31.1




[PATCH v2 18/25] iotests: switch to AQMP

2021-12-15 Thread John Snow
Simply import the type defition from the new location.

Signed-off-by: John Snow 
---
 tests/qemu-iotests/iotests.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 83bfedb902..cb21aebe36 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -37,7 +37,7 @@
 from contextlib import contextmanager
 
 from qemu.machine import qtest
-from qemu.qmp import QMPMessage
+from qemu.aqmp.legacy import QMPMessage
 
 # Use this logger for logging messages directly from the iotests module
 logger = logging.getLogger('qemu.iotests')
-- 
2.31.1




[PATCH v2 15/25] scripts/render-block-graph: switch to AQMP

2021-12-15 Thread John Snow
Creating an instance of qemu.aqmp.ExecuteError is too involved here, so
just drop the specificity down to a generic AQMPError.

Signed-off-by: John Snow 
---
 scripts/render_block_graph.py | 8 +++-
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py
index da6acf050d..97778927f3 100755
--- a/scripts/render_block_graph.py
+++ b/scripts/render_block_graph.py
@@ -25,10 +25,8 @@
 from graphviz import Digraph
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
-from qemu.qmp import (
-QEMUMonitorProtocol,
-QMPResponseError,
-)
+from qemu.aqmp import QMPError
+from qemu.aqmp.legacy import QEMUMonitorProtocol
 
 
 def perm(arr):
@@ -105,7 +103,7 @@ def command(self, cmd):
 reply = json.loads(subprocess.check_output(ar))
 
 if 'error' in reply:
-raise QMPResponseError(reply)
+raise QMPError(reply)
 
 return reply['return']
 
-- 
2.31.1




[PATCH v2 16/25] scripts/bench-block-job: switch to AQMP

2021-12-15 Thread John Snow
For this commit, we only need to remove accommodations for the
synchronous QMP library.

Signed-off-by: John Snow 
---
 scripts/simplebench/bench_block_job.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/scripts/simplebench/bench_block_job.py 
b/scripts/simplebench/bench_block_job.py
index a403c35b08..af9d1646a4 100755
--- a/scripts/simplebench/bench_block_job.py
+++ b/scripts/simplebench/bench_block_job.py
@@ -27,7 +27,6 @@
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
 from qemu.machine import QEMUMachine
-from qemu.qmp import QMPConnectError
 from qemu.aqmp import ConnectError
 
 
@@ -50,7 +49,7 @@ def bench_block_job(cmd, cmd_args, qemu_args):
 vm.launch()
 except OSError as e:
 return {'error': 'popen failed: ' + str(e)}
-except (QMPConnectError, ConnectError, socket.timeout):
+except (ConnectError, socket.timeout):
 return {'error': 'qemu failed: ' + str(vm.get_log())}
 
 try:
-- 
2.31.1




[PATCH v2 10/25] python: move qmp utilities to python/qemu/utils

2021-12-15 Thread John Snow
In order to upload a QMP package to PyPI, I want to remove any scripts
that I am not 100% confident I want to support upstream, beyond our
castle walls.

Move most of our QMP utilities into the utils package so we can split
them out from the PyPI upload.

Signed-off-by: John Snow 
---
 python/qemu/{qmp => utils}/qemu_ga_client.py |  0
 python/qemu/{qmp => utils}/qom.py|  0
 python/qemu/{qmp => utils}/qom_common.py |  0
 python/qemu/{qmp => utils}/qom_fuse.py   |  0
 python/setup.cfg | 16 
 scripts/qmp/qemu-ga-client   |  2 +-
 scripts/qmp/qom-fuse |  2 +-
 scripts/qmp/qom-get  |  2 +-
 scripts/qmp/qom-list |  2 +-
 scripts/qmp/qom-set  |  2 +-
 scripts/qmp/qom-tree |  2 +-
 11 files changed, 14 insertions(+), 14 deletions(-)
 rename python/qemu/{qmp => utils}/qemu_ga_client.py (100%)
 rename python/qemu/{qmp => utils}/qom.py (100%)
 rename python/qemu/{qmp => utils}/qom_common.py (100%)
 rename python/qemu/{qmp => utils}/qom_fuse.py (100%)

diff --git a/python/qemu/qmp/qemu_ga_client.py 
b/python/qemu/utils/qemu_ga_client.py
similarity index 100%
rename from python/qemu/qmp/qemu_ga_client.py
rename to python/qemu/utils/qemu_ga_client.py
diff --git a/python/qemu/qmp/qom.py b/python/qemu/utils/qom.py
similarity index 100%
rename from python/qemu/qmp/qom.py
rename to python/qemu/utils/qom.py
diff --git a/python/qemu/qmp/qom_common.py b/python/qemu/utils/qom_common.py
similarity index 100%
rename from python/qemu/qmp/qom_common.py
rename to python/qemu/utils/qom_common.py
diff --git a/python/qemu/qmp/qom_fuse.py b/python/qemu/utils/qom_fuse.py
similarity index 100%
rename from python/qemu/qmp/qom_fuse.py
rename to python/qemu/utils/qom_fuse.py
diff --git a/python/setup.cfg b/python/setup.cfg
index 417e937839..78421411d2 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -60,13 +60,13 @@ tui =
 
 [options.entry_points]
 console_scripts =
-qom = qemu.qmp.qom:main
-qom-set = qemu.qmp.qom:QOMSet.entry_point
-qom-get = qemu.qmp.qom:QOMGet.entry_point
-qom-list = qemu.qmp.qom:QOMList.entry_point
-qom-tree = qemu.qmp.qom:QOMTree.entry_point
-qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point [fuse]
-qemu-ga-client = qemu.qmp.qemu_ga_client:main
+qom = qemu.utils.qom:main
+qom-set = qemu.utils.qom:QOMSet.entry_point
+qom-get = qemu.utils.qom:QOMGet.entry_point
+qom-list = qemu.utils.qom:QOMList.entry_point
+qom-tree = qemu.utils.qom:QOMTree.entry_point
+qom-fuse = qemu.utils.qom_fuse:QOMFuse.entry_point [fuse]
+qemu-ga-client = qemu.utils.qemu_ga_client:main
 qmp-shell = qemu.qmp.qmp_shell:main
 aqmp-tui = qemu.aqmp.aqmp_tui:main [tui]
 
@@ -80,7 +80,7 @@ python_version = 3.6
 warn_unused_configs = True
 namespace_packages = True
 
-[mypy-qemu.qmp.qom_fuse]
+[mypy-qemu.utils.qom_fuse]
 # fusepy has no type stubs:
 allow_subclassing_any = True
 
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
index 102fd2cad9..56edd0234a 100755
--- a/scripts/qmp/qemu-ga-client
+++ b/scripts/qmp/qemu-ga-client
@@ -4,7 +4,7 @@ import os
 import sys
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import qemu_ga_client
+from qemu.utils import qemu_ga_client
 
 
 if __name__ == '__main__':
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
index a58c8ef979..d453807b27 100755
--- a/scripts/qmp/qom-fuse
+++ b/scripts/qmp/qom-fuse
@@ -4,7 +4,7 @@ import os
 import sys
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom_fuse import QOMFuse
+from qemu.utils.qom_fuse import QOMFuse
 
 
 if __name__ == '__main__':
diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get
index e4f3e0c013..04ebe052e8 100755
--- a/scripts/qmp/qom-get
+++ b/scripts/qmp/qom-get
@@ -4,7 +4,7 @@ import os
 import sys
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom import QOMGet
+from qemu.utils.qom import QOMGet
 
 
 if __name__ == '__main__':
diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list
index 7a071a54e1..853b85a8d3 100755
--- a/scripts/qmp/qom-list
+++ b/scripts/qmp/qom-list
@@ -4,7 +4,7 @@ import os
 import sys
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom import QOMList
+from qemu.utils.qom import QOMList
 
 
 if __name__ == '__main__':
diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set
index 9ca9e2ba10..06820feec4 100755
--- a/scripts/qmp/qom-set
+++ b/scripts/qmp/qom-set
@@ -4,7 +4,7 @@ import os
 import sys
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom import QOMSet
+from qemu.utils.qom import QOMSet
 
 
 if __name__ == '__main__':
diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree
index 

[PATCH v2 09/25] python/qmp: switch qmp-shell to AQMP

2021-12-15 Thread John Snow
We have a replacement for async QMP, but it doesn't have feature parity
yet. For now, then, port the old tool onto the new backend.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/legacy.py   |  3 +++
 python/qemu/qmp/qmp_shell.py | 31 +--
 2 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
index 27df22818a..0890f95b16 100644
--- a/python/qemu/aqmp/legacy.py
+++ b/python/qemu/aqmp/legacy.py
@@ -22,6 +22,9 @@
 from .qmp_client import QMPClient
 
 
+# (Temporarily) Re-export QMPBadPortError
+QMPBadPortError = qemu.qmp.QMPBadPortError
+
 #: QMPMessage is an entire QMP message of any kind.
 QMPMessage = Dict[str, Any]
 
diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py
index e7d7eb18f1..d11bf54b00 100644
--- a/python/qemu/qmp/qmp_shell.py
+++ b/python/qemu/qmp/qmp_shell.py
@@ -95,8 +95,13 @@
 Sequence,
 )
 
-from qemu import qmp
-from qemu.qmp import QMPMessage
+from qemu.aqmp import ConnectError, QMPError, SocketAddrT
+from qemu.aqmp.legacy import (
+QEMUMonitorProtocol,
+QMPBadPortError,
+QMPMessage,
+QMPObject,
+)
 
 
 LOG = logging.getLogger(__name__)
@@ -125,7 +130,7 @@ def complete(self, text: str, state: int) -> Optional[str]:
 return None
 
 
-class QMPShellError(qmp.QMPError):
+class QMPShellError(QMPError):
 """
 QMP Shell Base error class.
 """
@@ -153,7 +158,7 @@ def visit_Name(cls,  # pylint: disable=invalid-name
 return node
 
 
-class QMPShell(qmp.QEMUMonitorProtocol):
+class QMPShell(QEMUMonitorProtocol):
 """
 QMPShell provides a basic readline-based QMP shell.
 
@@ -161,7 +166,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
 :param pretty: Pretty-print QMP messages.
 :param verbose: Echo outgoing QMP messages to console.
 """
-def __init__(self, address: qmp.SocketAddrT,
+def __init__(self, address: SocketAddrT,
  pretty: bool = False, verbose: bool = False):
 super().__init__(address)
 self._greeting: Optional[QMPMessage] = None
@@ -237,7 +242,7 @@ def _parse_value(cls, val: str) -> object:
 
 def _cli_expr(self,
   tokens: Sequence[str],
-  parent: qmp.QMPObject) -> None:
+  parent: QMPObject) -> None:
 for arg in tokens:
 (key, sep, val) = arg.partition('=')
 if sep != '=':
@@ -403,7 +408,7 @@ class HMPShell(QMPShell):
 :param pretty: Pretty-print QMP messages.
 :param verbose: Echo outgoing QMP messages to console.
 """
-def __init__(self, address: qmp.SocketAddrT,
+def __init__(self, address: SocketAddrT,
  pretty: bool = False, verbose: bool = False):
 super().__init__(address, pretty, verbose)
 self._cpu_index = 0
@@ -512,19 +517,17 @@ def main() -> None:
 
 try:
 address = shell_class.parse_address(args.qmp_server)
-except qmp.QMPBadPortError:
+except QMPBadPortError:
 parser.error(f"Bad port number: {args.qmp_server}")
 return  # pycharm doesn't know error() is noreturn
 
 with shell_class(address, args.pretty, args.verbose) as qemu:
 try:
 qemu.connect(negotiate=not args.skip_negotiation)
-except qmp.QMPConnectError:
-die("Didn't get QMP greeting message")
-except qmp.QMPCapabilitiesError:
-die("Couldn't negotiate capabilities")
-except OSError as err:
-die(f"Couldn't connect to {args.qmp_server}: {err!s}")
+except ConnectError as err:
+if isinstance(err.exc, OSError):
+die(f"Couldn't connect to {args.qmp_server}: {err!s}")
+die(str(err))
 
 for _ in qemu.repl():
 pass
-- 
2.31.1




[PATCH v2 24/25] python: re-enable pylint duplicate-code warnings

2021-12-15 Thread John Snow
With the old library gone, there's nothing duplicated in the tree, so
the warning suppression can be removed.

Signed-off-by: John Snow 
---
 python/setup.cfg | 1 -
 1 file changed, 1 deletion(-)

diff --git a/python/setup.cfg b/python/setup.cfg
index 5140a5b322..c341e922c2 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -114,7 +114,6 @@ ignore_missing_imports = True
 disable=consider-using-f-string,
 too-many-function-args,  # mypy handles this with less false positives.
 no-member,  # mypy also handles this better.
-duplicate-code,  # To be removed by the end of this patch series.
 
 [pylint.basic]
 # Good variable names which should always be accepted, separated by a comma.
-- 
2.31.1




[PATCH v2 17/25] iotests/mirror-top-perms: switch to AQMP

2021-12-15 Thread John Snow
Signed-off-by: John Snow 

---

Note: I still need to adjust the logging. The problem now is that the
logging messages include the PID of the test process, so they need to be
filtered out. I'll investigate that for a follow-up, or for v2.

I could just add yet another filtering function somewhere, but I think
it's getting out of hand with how many filters and loggers there are, so
I want to give it a slightly more serious treatment instead of a
hackjob.

Signed-off-by: John Snow 
---
 tests/qemu-iotests/tests/mirror-top-perms | 7 +++
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/tests/qemu-iotests/tests/mirror-top-perms 
b/tests/qemu-iotests/tests/mirror-top-perms
index 0a51a613f3..f394931a00 100755
--- a/tests/qemu-iotests/tests/mirror-top-perms
+++ b/tests/qemu-iotests/tests/mirror-top-perms
@@ -23,7 +23,6 @@ import os
 
 from qemu.aqmp import ConnectError
 from qemu.machine import machine
-from qemu.qmp import QMPConnectError
 
 import iotests
 from iotests import change_log_level, qemu_img
@@ -101,13 +100,13 @@ class TestMirrorTopPerms(iotests.QMPTestCase):
 self.vm_b.add_device('virtio-blk,drive=drive0,share-rw=on')
 try:
 # Silence AQMP errors temporarily.
-# TODO: Remove this and just allow the errors to be logged when
-# AQMP fully replaces QMP.
+# TODO: Remove change_log_level and allow the errors to be logged.
+#   This necessitates a PID filter on *all* logging output.
 with change_log_level('qemu.aqmp'):
 self.vm_b.launch()
 print('ERROR: VM B launched successfully, '
   'this should not have happened')
-except (QMPConnectError, ConnectError):
+except ConnectError:
 assert 'Is another process using the image' in self.vm_b.get_log()
 
 result = self.vm.qmp('block-job-cancel',
-- 
2.31.1




[PATCH v2 08/25] python/qmp: switch qom tools to AQMP

2021-12-15 Thread John Snow
Signed-off-by: John Snow 
---
 python/qemu/qmp/qom.py|  5 +++--
 python/qemu/qmp/qom_common.py |  3 ++-
 python/qemu/qmp/qom_fuse.py   | 11 ++-
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/python/qemu/qmp/qom.py b/python/qemu/qmp/qom.py
index 8ff28a8343..bb5d1a78f5 100644
--- a/python/qemu/qmp/qom.py
+++ b/python/qemu/qmp/qom.py
@@ -32,7 +32,8 @@
 
 import argparse
 
-from . import QMPResponseError
+from qemu.aqmp import ExecuteError
+
 from .qom_common import QOMCommand
 
 
@@ -233,7 +234,7 @@ def _list_node(self, path: str) -> None:
 rsp = self.qmp.command('qom-get', path=path,
property=item.name)
 print(f"  {item.name}: {rsp} ({item.type})")
-except QMPResponseError as err:
+except ExecuteError as err:
 print(f"  {item.name}:  ({item.type})")
 print('')
 for item in items:
diff --git a/python/qemu/qmp/qom_common.py b/python/qemu/qmp/qom_common.py
index a59ae1a2a1..6f07451dfa 100644
--- a/python/qemu/qmp/qom_common.py
+++ b/python/qemu/qmp/qom_common.py
@@ -27,7 +27,8 @@
 TypeVar,
 )
 
-from . import QEMUMonitorProtocol, QMPError
+from qemu.aqmp import QMPError
+from qemu.aqmp.legacy import QEMUMonitorProtocol
 
 
 # The following is needed only for a type alias.
diff --git a/python/qemu/qmp/qom_fuse.py b/python/qemu/qmp/qom_fuse.py
index 43f4671fdb..653a76b93b 100644
--- a/python/qemu/qmp/qom_fuse.py
+++ b/python/qemu/qmp/qom_fuse.py
@@ -48,7 +48,8 @@
 import fuse
 from fuse import FUSE, FuseOSError, Operations
 
-from . import QMPResponseError
+from qemu.aqmp import ExecuteError
+
 from .qom_common import QOMCommand
 
 
@@ -99,7 +100,7 @@ def is_object(self, path: str) -> bool:
 try:
 self.qom_list(path)
 return True
-except QMPResponseError:
+except ExecuteError:
 return False
 
 def is_property(self, path: str) -> bool:
@@ -112,7 +113,7 @@ def is_property(self, path: str) -> bool:
 if item.name == prop:
 return True
 return False
-except QMPResponseError:
+except ExecuteError:
 return False
 
 def is_link(self, path: str) -> bool:
@@ -125,7 +126,7 @@ def is_link(self, path: str) -> bool:
 if item.name == prop and item.link:
 return True
 return False
-except QMPResponseError:
+except ExecuteError:
 return False
 
 def read(self, path: str, size: int, offset: int, fh: IO[bytes]) -> bytes:
@@ -138,7 +139,7 @@ def read(self, path: str, size: int, offset: int, fh: 
IO[bytes]) -> bytes:
 try:
 data = str(self.qmp.command('qom-get', path=path, property=prop))
 data += '\n'  # make values shell friendly
-except QMPResponseError as err:
+except ExecuteError as err:
 raise FuseOSError(EPERM) from err
 
 if offset > len(data):
-- 
2.31.1




[PATCH v2 14/25] scripts/cpu-x86-uarch-abi: switch to AQMP

2021-12-15 Thread John Snow
Signed-off-by: John Snow 
---
 scripts/cpu-x86-uarch-abi.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/cpu-x86-uarch-abi.py b/scripts/cpu-x86-uarch-abi.py
index 8963d90f0b..c262d2f027 100644
--- a/scripts/cpu-x86-uarch-abi.py
+++ b/scripts/cpu-x86-uarch-abi.py
@@ -6,7 +6,7 @@
 # compatibility levels for each CPU model.
 #
 
-from qemu import qmp
+from qemu.aqmp.legacy import QEMUMonitorProtocol
 import sys
 
 if len(sys.argv) != 2:
@@ -66,7 +66,7 @@
 
 
 sock = sys.argv[1]
-shell = qmp.QEMUMonitorProtocol(sock)
+shell = QEMUMonitorProtocol(sock)
 shell.connect()
 
 models = shell.cmd("query-cpu-definitions")
-- 
2.31.1




[PATCH v2 12/25] python/machine: permanently switch to AQMP

2021-12-15 Thread John Snow
Remove the QEMU_PYTHON_LEGACY_QMP environment variable, making the
switch permanent. Update Exceptions and import paths as necessary.

Signed-off-by: John Snow 
---
 python/qemu/machine/machine.py | 18 +++---
 python/qemu/machine/qtest.py   |  2 +-
 2 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py
index 67ab06ca2b..21fb4a4f30 100644
--- a/python/qemu/machine/machine.py
+++ b/python/qemu/machine/machine.py
@@ -40,21 +40,16 @@
 TypeVar,
 )
 
-from qemu.qmp import (  # pylint: disable=import-error
+from qemu.aqmp import SocketAddrT
+from qemu.aqmp.legacy import (
+QEMUMonitorProtocol,
 QMPMessage,
 QMPReturnValue,
-SocketAddrT,
 )
 
 from . import console_socket
 
 
-if os.environ.get('QEMU_PYTHON_LEGACY_QMP'):
-from qemu.qmp import QEMUMonitorProtocol
-else:
-from qemu.aqmp.legacy import QEMUMonitorProtocol
-
-
 LOG = logging.getLogger(__name__)
 
 
@@ -710,8 +705,9 @@ def events_wait(self,
 :param timeout: Optional timeout, in seconds.
 See QEMUMonitorProtocol.pull_event.
 
-:raise QMPTimeoutError: If timeout was non-zero and no matching events
-were found.
+:raise asyncio.TimeoutError:
+If timeout was non-zero and no matching events were found.
+
 :return: A QMP event matching the filter criteria.
  If timeout was 0 and no event matched, None.
 """
@@ -734,7 +730,7 @@ def _match(event: QMPMessage) -> bool:
 event = self._qmp.pull_event(wait=timeout)
 if event is None:
 # NB: None is only returned when timeout is false-ish.
-# Timeouts raise QMPTimeoutError instead!
+# Timeouts raise asyncio.TimeoutError instead!
 break
 if _match(event):
 return event
diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py
index f2f9aaa5e5..817c8a5425 100644
--- a/python/qemu/machine/qtest.py
+++ b/python/qemu/machine/qtest.py
@@ -26,7 +26,7 @@
 TextIO,
 )
 
-from qemu.qmp import SocketAddrT  # pylint: disable=import-error
+from qemu.aqmp.protocol import SocketAddrT
 
 from .machine import QEMUMachine
 
-- 
2.31.1




[PATCH v2 11/25] python: move qmp-shell under the AQMP package

2021-12-15 Thread John Snow
Signed-off-by: John Snow 
---
 python/README.rst  | 2 +-
 python/qemu/{qmp => aqmp}/qmp_shell.py | 0
 python/setup.cfg   | 2 +-
 scripts/qmp/qmp-shell  | 2 +-
 4 files changed, 3 insertions(+), 3 deletions(-)
 rename python/qemu/{qmp => aqmp}/qmp_shell.py (100%)

diff --git a/python/README.rst b/python/README.rst
index 9c1fceaee7..fcf74f69ea 100644
--- a/python/README.rst
+++ b/python/README.rst
@@ -59,7 +59,7 @@ Package installation also normally provides executable 
console scripts,
 so that tools like ``qmp-shell`` are always available via $PATH. To
 invoke them without installation, you can invoke e.g.:
 
-``> PYTHONPATH=~/src/qemu/python python3 -m qemu.qmp.qmp_shell``
+``> PYTHONPATH=~/src/qemu/python python3 -m qemu.aqmp.qmp_shell``
 
 The mappings between console script name and python module path can be
 found in ``setup.cfg``.
diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/aqmp/qmp_shell.py
similarity index 100%
rename from python/qemu/qmp/qmp_shell.py
rename to python/qemu/aqmp/qmp_shell.py
diff --git a/python/setup.cfg b/python/setup.cfg
index 78421411d2..168a79c867 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -67,7 +67,7 @@ console_scripts =
 qom-tree = qemu.utils.qom:QOMTree.entry_point
 qom-fuse = qemu.utils.qom_fuse:QOMFuse.entry_point [fuse]
 qemu-ga-client = qemu.utils.qemu_ga_client:main
-qmp-shell = qemu.qmp.qmp_shell:main
+qmp-shell = qemu.aqmp.qmp_shell:main
 aqmp-tui = qemu.aqmp.aqmp_tui:main [tui]
 
 [flake8]
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
index 4a20f97db7..31b19d73e2 100755
--- a/scripts/qmp/qmp-shell
+++ b/scripts/qmp/qmp-shell
@@ -4,7 +4,7 @@ import os
 import sys
 
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import qmp_shell
+from qemu.aqmp import qmp_shell
 
 
 if __name__ == '__main__':
-- 
2.31.1




[PATCH v2 13/25] scripts/cpu-x86-uarch-abi: fix CLI parsing

2021-12-15 Thread John Snow
Signed-off-by: John Snow 
---
 scripts/cpu-x86-uarch-abi.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/scripts/cpu-x86-uarch-abi.py b/scripts/cpu-x86-uarch-abi.py
index 08acc52a81..8963d90f0b 100644
--- a/scripts/cpu-x86-uarch-abi.py
+++ b/scripts/cpu-x86-uarch-abi.py
@@ -9,7 +9,7 @@
 from qemu import qmp
 import sys
 
-if len(sys.argv) != 1:
+if len(sys.argv) != 2:
 print("syntax: %s QMP-SOCK\n\n" % __file__ +
   "Where QMP-SOCK points to a QEMU process such as\n\n" +
   " # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait " +
@@ -66,7 +66,6 @@
 
 
 sock = sys.argv[1]
-cmd = sys.argv[2]
 shell = qmp.QEMUMonitorProtocol(sock)
 shell.connect()
 
-- 
2.31.1




[PATCH v2 03/25] python/aqmp: copy type definitions from qmp

2021-12-15 Thread John Snow
Copy the remaining type definitions from QMP into the qemu.aqmp.legacy
module. Now, most users don't need to import anything else but
qemu.aqmp.legacy.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/legacy.py   | 22 --
 python/qemu/aqmp/protocol.py | 16 ++--
 2 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
index 2ccb136b02..9431fe9330 100644
--- a/python/qemu/aqmp/legacy.py
+++ b/python/qemu/aqmp/legacy.py
@@ -6,7 +6,9 @@
 
 import asyncio
 from typing import (
+Any,
 Awaitable,
+Dict,
 List,
 Optional,
 TypeVar,
@@ -14,13 +16,29 @@
 )
 
 import qemu.qmp
-from qemu.qmp import QMPMessage, QMPReturnValue, SocketAddrT
 
 from .error import AQMPError
-from .protocol import Runstate
+from .protocol import Runstate, SocketAddrT
 from .qmp_client import QMPClient
 
 
+#: QMPMessage is an entire QMP message of any kind.
+QMPMessage = Dict[str, Any]
+
+#: QMPReturnValue is the 'return' value of a command.
+QMPReturnValue = object
+
+#: QMPObject is any object in a QMP message.
+QMPObject = Dict[str, object]
+
+# QMPMessage can be outgoing commands or incoming events/returns.
+# QMPReturnValue is usually a dict/json object, but due to QAPI's
+# 'returns-whitelist', it can actually be anything.
+#
+# {'return': {}} is a QMPMessage,
+# {} is the QMPReturnValue.
+
+
 # pylint: disable=missing-docstring
 
 
diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/aqmp/protocol.py
index 5190b33b13..42a897e2fe 100644
--- a/python/qemu/aqmp/protocol.py
+++ b/python/qemu/aqmp/protocol.py
@@ -46,6 +46,10 @@
 _TaskFN = Callable[[], Awaitable[None]]  # aka ``async def func() -> None``
 _FutureT = TypeVar('_FutureT', bound=Optional['asyncio.Future[Any]'])
 
+InternetAddrT = Tuple[str, int]
+UnixAddrT = str
+SocketAddrT = Union[UnixAddrT, InternetAddrT]
+
 
 class Runstate(Enum):
 """Protocol session runstate."""
@@ -257,7 +261,7 @@ async def runstate_changed(self) -> Runstate:
 
 @upper_half
 @require(Runstate.IDLE)
-async def accept(self, address: Union[str, Tuple[str, int]],
+async def accept(self, address: SocketAddrT,
  ssl: Optional[SSLContext] = None) -> None:
 """
 Accept a connection and begin processing message queues.
@@ -275,7 +279,7 @@ async def accept(self, address: Union[str, Tuple[str, int]],
 
 @upper_half
 @require(Runstate.IDLE)
-async def connect(self, address: Union[str, Tuple[str, int]],
+async def connect(self, address: SocketAddrT,
   ssl: Optional[SSLContext] = None) -> None:
 """
 Connect to the server and begin processing message queues.
@@ -337,7 +341,7 @@ def _set_state(self, state: Runstate) -> None:
 
 @upper_half
 async def _new_session(self,
-   address: Union[str, Tuple[str, int]],
+   address: SocketAddrT,
ssl: Optional[SSLContext] = None,
accept: bool = False) -> None:
 """
@@ -397,7 +401,7 @@ async def _new_session(self,
 @upper_half
 async def _establish_connection(
 self,
-address: Union[str, Tuple[str, int]],
+address: SocketAddrT,
 ssl: Optional[SSLContext] = None,
 accept: bool = False
 ) -> None:
@@ -424,7 +428,7 @@ async def _establish_connection(
 await self._do_connect(address, ssl)
 
 @upper_half
-async def _do_accept(self, address: Union[str, Tuple[str, int]],
+async def _do_accept(self, address: SocketAddrT,
  ssl: Optional[SSLContext] = None) -> None:
 """
 Acting as the transport server, accept a single connection.
@@ -482,7 +486,7 @@ async def _client_connected_cb(reader: asyncio.StreamReader,
 self.logger.debug("Connection accepted.")
 
 @upper_half
-async def _do_connect(self, address: Union[str, Tuple[str, int]],
+async def _do_connect(self, address: SocketAddrT,
   ssl: Optional[SSLContext] = None) -> None:
 """
 Acting as the transport client, initiate a connection to a server.
-- 
2.31.1




[PATCH v2 02/25] python/aqmp: handle asyncio.TimeoutError on execute()

2021-12-15 Thread John Snow
This exception can be injected into any await statement. If we are
canceled via timeout, we want to clear the pending execution record on
our way out.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/qmp_client.py | 8 ++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/python/qemu/aqmp/qmp_client.py b/python/qemu/aqmp/qmp_client.py
index 8105e29fa8..6a985ffe30 100644
--- a/python/qemu/aqmp/qmp_client.py
+++ b/python/qemu/aqmp/qmp_client.py
@@ -435,7 +435,11 @@ async def _issue(self, msg: Message) -> Union[None, str]:
 msg_id = msg['id']
 
 self._pending[msg_id] = asyncio.Queue(maxsize=1)
-await self._outgoing.put(msg)
+try:
+await self._outgoing.put(msg)
+except:
+del self._pending[msg_id]
+raise
 
 return msg_id
 
@@ -452,9 +456,9 @@ async def _reply(self, msg_id: Union[str, None]) -> Message:
 was lost, or some other problem.
 """
 queue = self._pending[msg_id]
-result = await queue.get()
 
 try:
+result = await queue.get()
 if isinstance(result, ExecInterruptedError):
 raise result
 return result
-- 
2.31.1




[PATCH v2 04/25] python/aqmp: add SocketAddrT to package root

2021-12-15 Thread John Snow
It's a commonly needed definition, it can be re-exported by the root.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/__init__.py | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/python/qemu/aqmp/__init__.py b/python/qemu/aqmp/__init__.py
index 880d5b6fa7..c6fa2dda58 100644
--- a/python/qemu/aqmp/__init__.py
+++ b/python/qemu/aqmp/__init__.py
@@ -26,7 +26,12 @@
 from .error import AQMPError
 from .events import EventListener
 from .message import Message
-from .protocol import ConnectError, Runstate, StateError
+from .protocol import (
+ConnectError,
+Runstate,
+SocketAddrT,
+StateError,
+)
 from .qmp_client import ExecInterruptedError, ExecuteError, QMPClient
 
 
@@ -48,4 +53,7 @@
 'ConnectError',
 'ExecuteError',
 'ExecInterruptedError',
+
+# Type aliases
+'SocketAddrT',
 )
-- 
2.31.1




[PATCH v2 05/25] python/aqmp: rename AQMPError to QMPError

2021-12-15 Thread John Snow
This is in preparation for renaming qemu.aqmp to qemu.qmp. I should have
done this from this from the very beginning, but it's a convenient time
to make sure this churn is taken care of.

Signed-off-by: John Snow 
---
 python/qemu/aqmp/__init__.py   |  6 +++---
 python/qemu/aqmp/error.py  | 12 ++--
 python/qemu/aqmp/events.py |  4 ++--
 python/qemu/aqmp/legacy.py |  4 ++--
 python/qemu/aqmp/protocol.py   |  8 
 python/qemu/aqmp/qmp_client.py |  8 
 6 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/python/qemu/aqmp/__init__.py b/python/qemu/aqmp/__init__.py
index c6fa2dda58..e1efab00af 100644
--- a/python/qemu/aqmp/__init__.py
+++ b/python/qemu/aqmp/__init__.py
@@ -6,7 +6,7 @@
 QEMU Guest Agent, and the QEMU Storage Daemon.
 
 `QMPClient` provides the main functionality of this package. All errors
-raised by this library dervive from `AQMPError`, see `aqmp.error` for
+raised by this library dervive from `QMPError`, see `aqmp.error` for
 additional detail. See `aqmp.events` for an in-depth tutorial on
 managing QMP events.
 """
@@ -23,7 +23,7 @@
 
 import logging
 
-from .error import AQMPError
+from .error import QMPError
 from .events import EventListener
 from .message import Message
 from .protocol import (
@@ -48,7 +48,7 @@
 'Runstate',
 
 # Exceptions, most generic to most explicit
-'AQMPError',
+'QMPError',
 'StateError',
 'ConnectError',
 'ExecuteError',
diff --git a/python/qemu/aqmp/error.py b/python/qemu/aqmp/error.py
index 781f49b008..24ba4d5054 100644
--- a/python/qemu/aqmp/error.py
+++ b/python/qemu/aqmp/error.py
@@ -1,21 +1,21 @@
 """
-AQMP Error Classes
+QMP Error Classes
 
 This package seeks to provide semantic error classes that are intended
 to be used directly by clients when they would like to handle particular
 semantic failures (e.g. "failed to connect") without needing to know the
 enumeration of possible reasons for that failure.
 
-AQMPError serves as the ancestor for all exceptions raised by this
+QMPError serves as the ancestor for all exceptions raised by this
 package, and is suitable for use in handling semantic errors from this
 library. In most cases, individual public methods will attempt to catch
 and re-encapsulate various exceptions to provide a semantic
 error-handling interface.
 
-.. admonition:: AQMP Exception Hierarchy Reference
+.. admonition:: QMP Exception Hierarchy Reference
 
  |   `Exception`
- |+-- `AQMPError`
+ |+-- `QMPError`
  | +-- `ConnectError`
  | +-- `StateError`
  | +-- `ExecInterruptedError`
@@ -31,11 +31,11 @@
 """
 
 
-class AQMPError(Exception):
+class QMPError(Exception):
 """Abstract error class for all errors originating from this package."""
 
 
-class ProtocolError(AQMPError):
+class ProtocolError(QMPError):
 """
 Abstract error class for protocol failures.
 
diff --git a/python/qemu/aqmp/events.py b/python/qemu/aqmp/events.py
index 5f7150c78d..f3d4e2b5e8 100644
--- a/python/qemu/aqmp/events.py
+++ b/python/qemu/aqmp/events.py
@@ -443,7 +443,7 @@ def accept(self, event) -> bool:
 Union,
 )
 
-from .error import AQMPError
+from .error import QMPError
 from .message import Message
 
 
@@ -451,7 +451,7 @@ def accept(self, event) -> bool:
 EventFilter = Callable[[Message], bool]
 
 
-class ListenerError(AQMPError):
+class ListenerError(QMPError):
 """
 Generic error class for `EventListener`-related problems.
 """
diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
index 9431fe9330..27df22818a 100644
--- a/python/qemu/aqmp/legacy.py
+++ b/python/qemu/aqmp/legacy.py
@@ -17,7 +17,7 @@
 
 import qemu.qmp
 
-from .error import AQMPError
+from .error import QMPError
 from .protocol import Runstate, SocketAddrT
 from .qmp_client import QMPClient
 
@@ -168,7 +168,7 @@ def __del__(self) -> None:
 # Nothing we can do about it now, but if we don't raise our
 # own error, the user will be treated to a lot of traceback
 # they might not understand.
-raise AQMPError(
+raise QMPError(
 "QEMUMonitorProtocol.close()"
 " was not called before object was garbage collected"
 )
diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/aqmp/protocol.py
index 42a897e2fe..9ee6fe4ae2 100644
--- a/python/qemu/aqmp/protocol.py
+++ b/python/qemu/aqmp/protocol.py
@@ -29,7 +29,7 @@
 cast,
 )
 
-from .error import AQMPError
+from .error import QMPError
 from .util import (
 bottom_half,
 create_task,
@@ -65,7 +65,7 @@ class Runstate(Enum):
 DISCONNECTING = 3
 
 
-class ConnectError(AQMPError):
+class ConnectError(QMPError):
 """
 Raised when the initial connection process has failed.
 
@@ -90,7 +90,7 @@ def __str__(self) -> str:
 return f"{self.error_message}: {cause}"
 
 
-class StateError(AQMPError):
+class StateError(QMPError):
 """
 An API command (connect, execute, etc) was 

[PATCH v2 06/25] python/qemu-ga-client: update instructions to newer CLI syntax

2021-12-15 Thread John Snow
Signed-off-by: John Snow 
---
 python/qemu/qmp/qemu_ga_client.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/python/qemu/qmp/qemu_ga_client.py 
b/python/qemu/qmp/qemu_ga_client.py
index 67ac0b4211..b3e1d98c9e 100644
--- a/python/qemu/qmp/qemu_ga_client.py
+++ b/python/qemu/qmp/qemu_ga_client.py
@@ -5,7 +5,7 @@
 
 Start QEMU with:
 
-# qemu [...] -chardev socket,path=/tmp/qga.sock,server,wait=off,id=qga0 \
+# qemu [...] -chardev socket,path=/tmp/qga.sock,server=on,wait=off,id=qga0 \
   -device virtio-serial \
   -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
 
-- 
2.31.1




[PATCH v2 07/25] python/qmp: switch qemu-ga-client to AQMP

2021-12-15 Thread John Snow
Signed-off-by: John Snow 
---
 python/qemu/qmp/qemu_ga_client.py | 22 +++---
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/python/qemu/qmp/qemu_ga_client.py 
b/python/qemu/qmp/qemu_ga_client.py
index b3e1d98c9e..15ed430c61 100644
--- a/python/qemu/qmp/qemu_ga_client.py
+++ b/python/qemu/qmp/qemu_ga_client.py
@@ -37,8 +37,8 @@
 # the COPYING file in the top-level directory.
 
 import argparse
+import asyncio
 import base64
-import errno
 import os
 import random
 import sys
@@ -50,8 +50,8 @@
 Sequence,
 )
 
-from qemu import qmp
-from qemu.qmp import SocketAddrT
+from qemu.aqmp import ConnectError, SocketAddrT
+from qemu.aqmp.legacy import QEMUMonitorProtocol
 
 
 # This script has not seen many patches or careful attention in quite
@@ -61,7 +61,7 @@
 # pylint: disable=missing-docstring
 
 
-class QemuGuestAgent(qmp.QEMUMonitorProtocol):
+class QemuGuestAgent(QEMUMonitorProtocol):
 def __getattr__(self, name: str) -> Callable[..., Any]:
 def wrapper(**kwds: object) -> object:
 return self.command('guest-' + name.replace('_', '-'), **kwds)
@@ -149,7 +149,7 @@ def ping(self, timeout: Optional[float]) -> bool:
 self.qga.settimeout(timeout)
 try:
 self.qga.ping()
-except TimeoutError:
+except asyncio.TimeoutError:
 return False
 return True
 
@@ -172,7 +172,7 @@ def suspend(self, mode: str) -> None:
 try:
 getattr(self.qga, 'suspend' + '_' + mode)()
 # On error exception will raise
-except TimeoutError:
+except asyncio.TimeoutError:
 # On success command will timed out
 return
 
@@ -182,7 +182,7 @@ def shutdown(self, mode: str = 'powerdown') -> None:
 
 try:
 self.qga.shutdown(mode=mode)
-except TimeoutError:
+except asyncio.TimeoutError:
 pass
 
 
@@ -277,7 +277,7 @@ def _cmd_reboot(client: QemuGuestAgentClient, args: 
Sequence[str]) -> None:
 
 def send_command(address: str, cmd: str, args: Sequence[str]) -> None:
 if not os.path.exists(address):
-print('%s not found' % address)
+print(f"'{address}' not found. (Is QEMU running?)")
 sys.exit(1)
 
 if cmd not in commands:
@@ -287,10 +287,10 @@ def send_command(address: str, cmd: str, args: 
Sequence[str]) -> None:
 
 try:
 client = QemuGuestAgentClient(address)
-except OSError as err:
+except ConnectError as err:
 print(err)
-if err.errno == errno.ECONNREFUSED:
-print('Hint: qemu is not running?')
+if isinstance(err.exc, ConnectionError):
+print('(Is QEMU running?)')
 sys.exit(1)
 
 if cmd == 'fsfreeze' and args[0] == 'freeze':
-- 
2.31.1




[PATCH v2 01/25] python/aqmp: add __del__ method to legacy interface

2021-12-15 Thread John Snow
asyncio can complain *very* loudly if you forget to back out of things
gracefully before the garbage collector starts destroying objects that
contain live references to asyncio Tasks.

The usual fix is just to remember to call aqmp.disconnect(), but for the
sake of the legacy wrapper and quick, one-off scripts where a graceful
shutdown is not necessarily of paramount imporance, add a courtesy
cleanup that will trigger prior to seeing screenfuls of confusing
asyncio tracebacks.

Note that we can't *always* save you from yourself; depending on when
the GC runs, you might just seriously be out of luck. The best we can do
in this case is to gently remind you to clean up after yourself.

(Still much better than multiple pages of incomprehensible python
warnings for the crime of forgetting to put your toys away.)

Signed-off-by: John Snow 
---
 python/qemu/aqmp/legacy.py | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
index 9e7b9fb80b..2ccb136b02 100644
--- a/python/qemu/aqmp/legacy.py
+++ b/python/qemu/aqmp/legacy.py
@@ -16,6 +16,8 @@
 import qemu.qmp
 from qemu.qmp import QMPMessage, QMPReturnValue, SocketAddrT
 
+from .error import AQMPError
+from .protocol import Runstate
 from .qmp_client import QMPClient
 
 
@@ -136,3 +138,19 @@ def settimeout(self, timeout: Optional[float]) -> None:
 
 def send_fd_scm(self, fd: int) -> None:
 self._aqmp.send_fd_scm(fd)
+
+def __del__(self) -> None:
+if self._aqmp.runstate == Runstate.IDLE:
+return
+
+if not self._aloop.is_running():
+self.close()
+else:
+# Garbage collection ran while the event loop was running.
+# Nothing we can do about it now, but if we don't raise our
+# own error, the user will be treated to a lot of traceback
+# they might not understand.
+raise AQMPError(
+"QEMUMonitorProtocol.close()"
+" was not called before object was garbage collected"
+)
-- 
2.31.1




[PATCH v2 00/25] Python: delete synchronous qemu.qmp package

2021-12-15 Thread John Snow
GitLab: https://gitlab.com/jsnow/qemu/-/commits/python-qmp-legacy-switch
CI: https://gitlab.com/jsnow/qemu/-/pipelines/430491195

Hi, this series is part of an effort to publish the qemu.qmp package on
PyPI. It is the first of three series to complete this work:

--> (1) Switch the new Async QMP library in to python/qemu/qmp
(2) Fork python/qemu/qmp out into its own repository,
with updated GitLab CI/CD targets to build packages.
(3) Update qemu.git to install qemu.qmp from PyPI,
and then delete python/qemu/qmp.

This series swaps out qemu.qmp for qemu.aqmp permanently, instead of
hiding it behind an environment variable toggle. This leaves us with
just one QMP library to worry about. It also implements the rename of
"qemu.aqmp" to "qemu.qmp".

Who should review what?
  - iotests/block maintainers (Hanna, Vladimir, and Kevin):
please look at patches 15-18.
  - Dan, please glance at patches 13-14.
  - Anyone who cares about Python: whatever you're willing to
stomach. We're recently down Willian and Eduardo, so there's not
many dedicated Python reviewers...

I suspect the most potential disruption to iotest and avocado
maintainers, as those two subsystems rely on the QMP features the
most. Would appreciate at least an ACK from each of those camps if
you're willing to give benefit-of-the-doubt on the actual Python code.

V2:
 - Integrate the renaming of qemu.aqmp to qemu.qmp in this series
 - Minor bits and pieces.

John Snow (25):
  python/aqmp: add __del__ method to legacy interface
  python/aqmp: handle asyncio.TimeoutError on execute()
  python/aqmp: copy type definitions from qmp
  python/aqmp: add SocketAddrT to package root
  python/aqmp: rename AQMPError to QMPError
  python/qemu-ga-client: update instructions to newer CLI syntax
  python/qmp: switch qemu-ga-client to AQMP
  python/qmp: switch qom tools to AQMP
  python/qmp: switch qmp-shell to AQMP
  python: move qmp utilities to python/qemu/utils
  python: move qmp-shell under the AQMP package
  python/machine: permanently switch to AQMP
  scripts/cpu-x86-uarch-abi: fix CLI parsing
  scripts/cpu-x86-uarch-abi: switch to AQMP
  scripts/render-block-graph: switch to AQMP
  scripts/bench-block-job: switch to AQMP
  iotests/mirror-top-perms: switch to AQMP
  iotests: switch to AQMP
  python: temporarily silence pylint duplicate-code warnings
  python/aqmp: take QMPBadPortError and parse_address from qemu.qmp
  python/aqmp: fully separate from qmp.QEMUMonitorProtocol
  python/aqmp: copy qmp docstrings to qemu.aqmp.legacy
  python: remove the old QMP package
  python: re-enable pylint duplicate-code warnings
  python: rename qemu.aqmp to qemu.qmp

 python/qemu/qmp/README.rst   |   9 -
 python/qemu/aqmp/__init__.py |  51 ---
 python/qemu/aqmp/legacy.py   | 138 --
 python/qemu/aqmp/py.typed|   0
 python/qemu/machine/machine.py   |  18 +-
 python/qemu/machine/qtest.py |   2 +-
 python/qemu/qmp/__init__.py  | 441 ++-
 python/qemu/{aqmp => qmp}/aqmp_tui.py|   2 +-
 python/qemu/{aqmp => qmp}/error.py   |  12 +-
 python/qemu/{aqmp => qmp}/events.py  |   6 +-
 python/qemu/qmp/legacy.py| 319 ++
 python/qemu/{aqmp => qmp}/message.py |   0
 python/qemu/{aqmp => qmp}/models.py  |   0
 python/qemu/{aqmp => qmp}/protocol.py|  28 +-
 python/qemu/{aqmp => qmp}/qmp_client.py  |  32 +-
 python/qemu/qmp/qmp_shell.py |  31 +-
 python/qemu/{aqmp => qmp}/util.py|   0
 python/qemu/{qmp => utils}/qemu_ga_client.py |  24 +-
 python/qemu/{qmp => utils}/qom.py|   5 +-
 python/qemu/{qmp => utils}/qom_common.py |   3 +-
 python/qemu/{qmp => utils}/qom_fuse.py   |  11 +-
 python/setup.cfg |  21 +-
 python/tests/protocol.py |  14 +-
 scripts/cpu-x86-uarch-abi.py |   7 +-
 scripts/device-crash-test|   4 +-
 scripts/qmp/qemu-ga-client   |   2 +-
 scripts/qmp/qom-fuse |   2 +-
 scripts/qmp/qom-get  |   2 +-
 scripts/qmp/qom-list |   2 +-
 scripts/qmp/qom-set  |   2 +-
 scripts/qmp/qom-tree |   2 +-
 scripts/render_block_graph.py|   8 +-
 scripts/simplebench/bench_block_job.py   |   5 +-
 tests/qemu-iotests/iotests.py|   2 +-
 tests/qemu-iotests/tests/mirror-top-perms|  13 +-
 35 files changed, 490 insertions(+), 728 deletions(-)
 delete mode 100644 python/qemu/qmp/README.rst
 delete mode 100644 python/qemu/aqmp/__init__.py
 delete mode 100644 python/qemu/aqmp/legacy.py
 delete mode 100644 python/qemu/aqmp/py.typed
 rename python/qemu/{aqmp => qmp}/aqmp_tui.py (99%)
 rename python/qemu/{aqmp => qmp}/error.py (87%)
 rename python/qemu/{aqmp 

Re: [PATCH v3 00/28] glib: Replace g_memdup() by g_memdup2()

2021-12-15 Thread Philippe Mathieu-Daudé
Hi Laurent,

On 9/3/21 19:44, Philippe Mathieu-Daudé wrote:

> This series provides the safely equivalent g_memdup2() wrapper,
> and replace all g_memdup() calls by it.

> Philippe Mathieu-Daudé (28):
>   hw/hyperv/vmbus: Remove unused vmbus_load/save_req()
>   glib-compat: Introduce g_memdup2() wrapper
>   qapi: Replace g_memdup() by g_memdup2()
>   accel/tcg: Replace g_memdup() by g_memdup2()
>   block/qcow2-bitmap: Replace g_memdup() by g_memdup2()
>   softmmu: Replace g_memdup() by g_memdup2()
>   hw/9pfs: Replace g_memdup() by g_memdup2()
>   hw/acpi: Avoid truncating acpi_data_len() to 32-bit
>   hw/acpi: Replace g_memdup() by g_memdup2()
>   hw/core/machine: Replace g_memdup() by g_memdup2()
>   hw/hppa/machine: Replace g_memdup() by g_memdup2()
>   hw/i386/multiboot: Replace g_memdup() by g_memdup2()
>   hw/net/eepro100: Replace g_memdup() by g_memdup2()
>   hw/nvram/fw_cfg: Replace g_memdup() by g_memdup2()
>   hw/scsi/mptsas: Replace g_memdup() by g_memdup2()
>   hw/ppc/spapr_pci: Replace g_memdup() by g_memdup2()
>   hw/rdma: Replace g_memdup() by g_memdup2()
>   hw/vfio/pci: Replace g_memdup() by g_memdup2()
>   hw/virtio: Replace g_memdup() by g_memdup2()
>   net/colo: Replace g_memdup() by g_memdup2()
>   ui/clipboard: Replace g_memdup() by g_memdup2()
>   linux-user: Replace g_memdup() by g_memdup2()
>   tests/unit: Replace g_memdup() by g_memdup2()
>   tests/qtest: Replace g_memdup() by g_memdup2()
>   target/arm: Replace g_memdup() by g_memdup2()
>   target/ppc: Replace g_memdup() by g_memdup2()
>   contrib: Replace g_memdup() by g_memdup2()
>   checkpatch: Do not allow deprecated g_memdup(
Is it possible to take the reviewed patches 2, 24 and 28
via qemu-trivial, so the rest can be merged slowly by
each submaintainer?




Re: [PATCH v2] block-backend: prevent dangling BDS pointers across aio_poll()

2021-12-15 Thread Stefan Hajnoczi
On Wed, Dec 15, 2021 at 04:31:26PM +0100, Kevin Wolf wrote:
> Am 15.12.2021 um 12:28 hat Stefan Hajnoczi geschrieben:
> > On Tue, Dec 14, 2021 at 03:59:49PM +0100, Kevin Wolf wrote:
> > > Am 14.12.2021 um 15:35 hat Stefan Hajnoczi geschrieben:
> > > > The BlockBackend root child can change when aio_poll() is invoked. This
> > > > happens when a temporary filter node is removed upon blockjob
> > > > completion, for example.
> > > > 
> > > > Functions in block/block-backend.c must be aware of this when using a
> > > > blk_bs() pointer across aio_poll() because the BlockDriverState refcnt
> > > > may reach 0, resulting in a stale pointer.
> > > > 
> > > > One example is scsi_device_purge_requests(), which calls blk_drain() to
> > > > wait for in-flight requests to cancel. If the backup blockjob is active,
> > > > then the BlockBackend root child is a temporary filter BDS owned by the
> > > > blockjob. The blockjob can complete during bdrv_drained_begin() and the
> > > > last reference to the BDS is released when the temporary filter node is
> > > > removed. This results in a use-after-free when blk_drain() calls
> > > > bdrv_drained_end(bs) on the dangling pointer.
> > > > 
> > > > Explicitly hold a reference to bs across block APIs that invoke
> > > > aio_poll().
> > > > 
> > > > Signed-off-by: Stefan Hajnoczi 
> > > > ---
> > > > v2:
> > > > - Audit block/block-backend.c and fix additional cases
> > > > ---
> > > >  block/block-backend.c | 11 +++
> > > >  1 file changed, 11 insertions(+)
> > > > 
> > > > diff --git a/block/block-backend.c b/block/block-backend.c
> > > > index 12ef80ea17..a40ad7fa92 100644
> > > > --- a/block/block-backend.c
> > > > +++ b/block/block-backend.c
> > > > @@ -828,10 +828,12 @@ void blk_remove_bs(BlockBackend *blk)
> > > >  notifier_list_notify(>remove_bs_notifiers, blk);
> > > >  if (tgm->throttle_state) {
> > > >  bs = blk_bs(blk);
> > > > +bdrv_ref(bs);
> > > >  bdrv_drained_begin(bs);
> > > >  throttle_group_detach_aio_context(tgm);
> > > >  throttle_group_attach_aio_context(tgm, qemu_get_aio_context());
> > > >  bdrv_drained_end(bs);
> > > > +bdrv_unref(bs);
> > > >  }
> > > >  
> > > >  blk_update_root_state(blk);
> > > 
> > > This hunk is unnecessary, we still hold a reference that is only given
> > > up a few lines down with bdrv_root_unref_child(root).
> > 
> > That's not the only place where the reference can be dropped:
> > bdrv_drop_filter() removes the filter node from the graph.
> > 
> > Here is a case where it happens: block/backup.c:backup_clean() ->
> > bdrv_cbw_drop() -> bdrv_drop_filter() -> bdrv_replace_node_common() ->
> > bdrv_replace_child_commit(). After we reach this bdrv_unref() is called
> > a few times and all references are dropped because the node is no longer
> > in the graph.
> > 
> > This happens during blk_remove_bs() -> bdrv_drained_begin(), so the bs
> > pointer in the above hunk can be stale.
> 
> Is the scenario that blk->root doesn't go away, but the node it
> references changes? So the unref below will be for a different node than
> we're draining here?

Yes, exactly.

> If so, let's add a comment that blk_bs(blk) can change after the drain,
> and maybe move the BlockDriverState *bs declaration inside the if block
> because the variable is invalid after it anyway.

Will fix.

> Can it also happen that instead of removing a node from the chain, a new
> one is inserted and we actually end up having drained the wrong node
> before switching the context for tgm?

I'll investigate that. There is already some level of support for
draining new nodes but I'm not sure it covers all insert and replace
cases.

Stefan


signature.asc
Description: PGP signature


Re: [PATCH v2] block-backend: prevent dangling BDS pointers across aio_poll()

2021-12-15 Thread Kevin Wolf
Am 15.12.2021 um 12:28 hat Stefan Hajnoczi geschrieben:
> On Tue, Dec 14, 2021 at 03:59:49PM +0100, Kevin Wolf wrote:
> > Am 14.12.2021 um 15:35 hat Stefan Hajnoczi geschrieben:
> > > The BlockBackend root child can change when aio_poll() is invoked. This
> > > happens when a temporary filter node is removed upon blockjob
> > > completion, for example.
> > > 
> > > Functions in block/block-backend.c must be aware of this when using a
> > > blk_bs() pointer across aio_poll() because the BlockDriverState refcnt
> > > may reach 0, resulting in a stale pointer.
> > > 
> > > One example is scsi_device_purge_requests(), which calls blk_drain() to
> > > wait for in-flight requests to cancel. If the backup blockjob is active,
> > > then the BlockBackend root child is a temporary filter BDS owned by the
> > > blockjob. The blockjob can complete during bdrv_drained_begin() and the
> > > last reference to the BDS is released when the temporary filter node is
> > > removed. This results in a use-after-free when blk_drain() calls
> > > bdrv_drained_end(bs) on the dangling pointer.
> > > 
> > > Explicitly hold a reference to bs across block APIs that invoke
> > > aio_poll().
> > > 
> > > Signed-off-by: Stefan Hajnoczi 
> > > ---
> > > v2:
> > > - Audit block/block-backend.c and fix additional cases
> > > ---
> > >  block/block-backend.c | 11 +++
> > >  1 file changed, 11 insertions(+)
> > > 
> > > diff --git a/block/block-backend.c b/block/block-backend.c
> > > index 12ef80ea17..a40ad7fa92 100644
> > > --- a/block/block-backend.c
> > > +++ b/block/block-backend.c
> > > @@ -828,10 +828,12 @@ void blk_remove_bs(BlockBackend *blk)
> > >  notifier_list_notify(>remove_bs_notifiers, blk);
> > >  if (tgm->throttle_state) {
> > >  bs = blk_bs(blk);
> > > +bdrv_ref(bs);
> > >  bdrv_drained_begin(bs);
> > >  throttle_group_detach_aio_context(tgm);
> > >  throttle_group_attach_aio_context(tgm, qemu_get_aio_context());
> > >  bdrv_drained_end(bs);
> > > +bdrv_unref(bs);
> > >  }
> > >  
> > >  blk_update_root_state(blk);
> > 
> > This hunk is unnecessary, we still hold a reference that is only given
> > up a few lines down with bdrv_root_unref_child(root).
> 
> That's not the only place where the reference can be dropped:
> bdrv_drop_filter() removes the filter node from the graph.
> 
> Here is a case where it happens: block/backup.c:backup_clean() ->
> bdrv_cbw_drop() -> bdrv_drop_filter() -> bdrv_replace_node_common() ->
> bdrv_replace_child_commit(). After we reach this bdrv_unref() is called
> a few times and all references are dropped because the node is no longer
> in the graph.
> 
> This happens during blk_remove_bs() -> bdrv_drained_begin(), so the bs
> pointer in the above hunk can be stale.

Is the scenario that blk->root doesn't go away, but the node it
references changes? So the unref below will be for a different node than
we're draining here?

If so, let's add a comment that blk_bs(blk) can change after the drain,
and maybe move the BlockDriverState *bs declaration inside the if block
because the variable is invalid after it anyway.

Can it also happen that instead of removing a node from the chain, a new
one is inserted and we actually end up having drained the wrong node
before switching the context for tgm?

Kevin


signature.asc
Description: PGP signature


[PATCH] block/file-posix: Simplify the XFS_IOC_DIOINFO handling

2021-12-15 Thread Thomas Huth
The handling for the XFS_IOC_DIOINFO ioctl is currently quite excessive:
This is not a "real" feature like the other features that we provide with
the "--enable-xxx" and "--disable-xxx" switches for the configure script,
since this does not influence lots of code (it's only about one call to
xfsctl() in file-posix.c), so people don't gain much with the ability to
disable this with "--disable-xfsctl".
It's also unfortunate that the ioctl will be disabled on Linux in case
the user did not install the right xfsprogs-devel package before running
configure. Thus let's simplify this by providing the ioctl definition
on our own, so we can completely get rid of the header dependency and
thus the related code in the configure script.

Suggested-by: Paolo Bonzini 
Signed-off-by: Thomas Huth 
---
 block/file-posix.c | 37 -
 configure  | 31 ---
 meson.build|  1 -
 3 files changed, 16 insertions(+), 53 deletions(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index b283093e5b..1f1756e192 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -106,10 +106,6 @@
 #include 
 #endif
 
-#ifdef CONFIG_XFS
-#include 
-#endif
-
 /* OS X does not have O_DSYNC */
 #ifndef O_DSYNC
 #ifdef O_SYNC
@@ -156,9 +152,6 @@ typedef struct BDRVRawState {
 int perm_change_flags;
 BDRVReopenState *reopen_state;
 
-#ifdef CONFIG_XFS
-bool is_xfs:1;
-#endif
 bool has_discard:1;
 bool has_write_zeroes:1;
 bool discard_zeroes:1;
@@ -409,14 +402,22 @@ static void raw_probe_alignment(BlockDriverState *bs, int 
fd, Error **errp)
 if (probe_logical_blocksize(fd, >bl.request_alignment) < 0) {
 bs->bl.request_alignment = 0;
 }
-#ifdef CONFIG_XFS
-if (s->is_xfs) {
-struct dioattr da;
-if (xfsctl(NULL, fd, XFS_IOC_DIOINFO, ) >= 0) {
-bs->bl.request_alignment = da.d_miniosz;
-/* The kernel returns wrong information for d_mem */
-/* s->buf_align = da.d_mem; */
-}
+
+#ifdef __linux__
+/*
+ * The XFS ioctl definitions are shipped in extra packages that might
+ * not always be available. Since we just need the XFS_IOC_DIOINFO ioctl
+ * here, we simply use our own definition instead:
+ */
+struct xfs_dioattr {
+uint32_t d_mem;
+uint32_t d_miniosz;
+uint32_t d_maxiosz;
+} da;
+if (ioctl(fd, _IOR('X', 30, struct xfs_dioattr), ) >= 0) {
+bs->bl.request_alignment = da.d_miniosz;
+/* The kernel returns wrong information for d_mem */
+/* s->buf_align = da.d_mem; */
 }
 #endif
 
@@ -798,12 +799,6 @@ static int raw_open_common(BlockDriverState *bs, QDict 
*options,
 #endif
 s->needs_alignment = raw_needs_alignment(bs);
 
-#ifdef CONFIG_XFS
-if (platform_test_xfs_fd(s->fd)) {
-s->is_xfs = true;
-}
-#endif
-
 bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
 if (S_ISREG(st.st_mode)) {
 /* When extending regular files, we get zeros from the OS */
diff --git a/configure b/configure
index bb99a40ed0..e0d8bedd3a 100755
--- a/configure
+++ b/configure
@@ -291,7 +291,6 @@ EXTRA_CXXFLAGS=""
 EXTRA_LDFLAGS=""
 
 xen_ctrl_version="$default_feature"
-xfs="$default_feature"
 membarrier="$default_feature"
 vhost_kernel="$default_feature"
 vhost_net="$default_feature"
@@ -1020,10 +1019,6 @@ for opt do
   ;;
   --enable-opengl) opengl="yes"
   ;;
-  --disable-xfsctl) xfs="no"
-  ;;
-  --enable-xfsctl) xfs="yes"
-  ;;
   --disable-zlib-test)
   ;;
   --enable-guest-agent) guest_agent="yes"
@@ -1448,7 +1443,6 @@ cat << EOF
   avx512f AVX512F optimization support
   replication replication support
   opengl  opengl support
-  xfsctl  xfsctl support
   qom-cast-debug  cast debugging support
   tools   build qemu-io, qemu-nbd and qemu-img tools
   bochs   bochs image format support
@@ -2340,28 +2334,6 @@ EOF
 fi
 fi
 
-##
-# xfsctl() probe, used for file-posix.c
-if test "$xfs" != "no" ; then
-  cat > $TMPC << EOF
-#include   /* NULL */
-#include 
-int main(void)
-{
-xfsctl(NULL, 0, 0, NULL);
-return 0;
-}
-EOF
-  if compile_prog "" "" ; then
-xfs="yes"
-  else
-if test "$xfs" = "yes" ; then
-  feature_not_found "xfs" "Install xfsprogs/xfslibs devel"
-fi
-xfs=no
-  fi
-fi
-
 ##
 # plugin linker support probe
 
@@ -3478,9 +3450,6 @@ echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" 
>> $config_host_mak
 if test "$block_drv_whitelist_tools" = "yes" ; then
   echo "CONFIG_BDRV_WHITELIST_TOOLS=y" >> $config_host_mak
 fi
-if test "$xfs" = "yes" ; then
-  echo "CONFIG_XFS=y" >> $config_host_mak
-fi
 qemu_version=$(head $source_path/VERSION)
 echo "PKGVERSION=$pkgversion" >>$config_host_mak
 echo "SRC_PATH=$source_path" >> $config_host_mak
diff --git a/meson.build b/meson.build
index 

Re: [RFC PATCH 0/6] Removal of Aiocontext lock and usage of subtree drains in aborted transactions

2021-12-15 Thread Hanna Reitz

On 14.12.21 19:10, Emanuele Giuseppe Esposito wrote:



On 13/12/2021 15:52, Stefan Hajnoczi wrote:

Off-topic: I don't understand the difference between the effects of
bdrv_drained_begin() and bdrv_subtree_drained_begin(). Both call
aio_disable_external(aio_context) and aio_poll(). bdrv_drained_begin()
only polls parents and itself, while bdrv_subtree_drained_begin() also
polls children. But why does that distinction matter? I wouldn't know
when to use one over the other.


Good point. Now I am wondering the same, so it would be great if 
anyone could clarify it.


As far as I understand, bdrv_drained_begin() is used to drain and stop 
requests on a single BDS, whereas bdrv_subtree_drained_begin() drains 
the BDS and all of its children.  So when you don’t care about lingering 
requests in child nodes, then bdrv_drained_begin() suffices.


Hanna




[PATCH v3 1/3] block_int: make bdrv_backing_overridden static

2021-12-15 Thread Emanuele Giuseppe Esposito
bdrv_backing_overridden is only used in block.c, so there is
no need to leave it in block_int.h

Signed-off-by: Emanuele Giuseppe Esposito 
Reviewed-by: Stefan Hajnoczi 
---
 block.c   | 4 +++-
 include/block/block_int.h | 3 ---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/block.c b/block.c
index 0ac5b163d2..10346b5011 100644
--- a/block.c
+++ b/block.c
@@ -103,6 +103,8 @@ static int bdrv_reopen_prepare(BDRVReopenState 
*reopen_state,
 static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
 static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
 
+static bool bdrv_backing_overridden(BlockDriverState *bs);
+
 /* If non-zero, use only whitelisted block drivers */
 static int use_bdrv_whitelist;
 
@@ -7475,7 +7477,7 @@ static bool append_strong_runtime_options(QDict *d, 
BlockDriverState *bs)
 /* Note: This function may return false positives; it may return true
  * even if opening the backing file specified by bs's image header
  * would result in exactly bs->backing. */
-bool bdrv_backing_overridden(BlockDriverState *bs)
+static bool bdrv_backing_overridden(BlockDriverState *bs)
 {
 if (bs->backing) {
 return strcmp(bs->auto_backing_file,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index f4c75e8ba9..27008cfb22 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -1122,9 +1122,6 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int 
buf_size,
 void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
   QDict *options);
 
-bool bdrv_backing_overridden(BlockDriverState *bs);
-
-
 /**
  * bdrv_add_aio_context_notifier:
  *
-- 
2.31.1




[PATCH v3 0/3] block: minor refactoring in preparation to the block layer API split

2021-12-15 Thread Emanuele Giuseppe Esposito
These patches are taken from my old patches and feedback of
my series "block layer: split block APIs in global state and I/O".

The reason for a separate series is that the original one is
already too long, and these patches are just refactoring the code,
mainly deleting or moving functions in blockdev.h and block_int.h.

Signed-off-by: Emanuele Giuseppe Esposito 
---
v3:
* Apply Kevin comments, remove getter method added in v2 and do
  not touch drive_add().

v2:
* Apply Philippe comments, instead of renaming a make if_name
  public, create a getter method (discard old patch 2).

Emanuele Giuseppe Esposito (3):
  block_int: make bdrv_backing_overridden static
  include/sysemu/blockdev.h: remove drive_mark_claimed_by_board and
inline drive_def
  include/sysemu/blockdev.h: remove drive_get_max_devs

 block.c|  4 +++-
 block/monitor/block-hmp-cmds.c |  2 +-
 blockdev.c | 24 +---
 include/block/block_int.h  |  3 ---
 include/sysemu/blockdev.h  |  3 ---
 softmmu/vl.c   |  4 +++-
 6 files changed, 8 insertions(+), 32 deletions(-)

-- 
2.31.1




[PATCH v3 3/3] include/sysemu/blockdev.h: remove drive_get_max_devs

2021-12-15 Thread Emanuele Giuseppe Esposito
Remove drive_get_max_devs, as it is not used by anyone.

Last use was removed in commit 8f2d75e81d5
("hw: Drop superfluous special checks for orphaned -drive").

Signed-off-by: Emanuele Giuseppe Esposito 
Reviewed-by: Philippe Mathieu-Daudé 
Reviewed-by: Stefan Hajnoczi 
---
 blockdev.c| 17 -
 include/sysemu/blockdev.h |  1 -
 2 files changed, 18 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 9839e234cb..5f332c6ebd 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -168,23 +168,6 @@ void blockdev_auto_del(BlockBackend *blk)
 }
 }
 
-/**
- * Returns the current mapping of how many units per bus
- * a particular interface can support.
- *
- *  A positive integer indicates n units per bus.
- *  0 implies the mapping has not been established.
- * -1 indicates an invalid BlockInterfaceType was given.
- */
-int drive_get_max_devs(BlockInterfaceType type)
-{
-if (type >= IF_IDE && type < IF_COUNT) {
-return if_max_devs[type];
-}
-
-return -1;
-}
-
 static int drive_index_to_bus_id(BlockInterfaceType type, int index)
 {
 int max_devs = if_max_devs[type];
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
index b236425c6e..9e4c68b6ca 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -48,7 +48,6 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int 
unit);
 void drive_check_orphaned(void);
 DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
 int drive_get_max_bus(BlockInterfaceType type);
-int drive_get_max_devs(BlockInterfaceType type);
 DriveInfo *drive_get_next(BlockInterfaceType type);
 
 QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
-- 
2.31.1




[PATCH v3 2/3] include/sysemu/blockdev.h: remove drive_mark_claimed_by_board and inline drive_def

2021-12-15 Thread Emanuele Giuseppe Esposito
drive_def is only a particular use case of
qemu_opts_parse_noisily, so it can be inlined.

Also remove drive_mark_claimed_by_board, as it is only defined
but not implemented (nor used) anywhere.

Signed-off-by: Emanuele Giuseppe Esposito 
---
 block/monitor/block-hmp-cmds.c | 2 +-
 blockdev.c | 7 +--
 include/sysemu/blockdev.h  | 2 --
 softmmu/vl.c   | 4 +++-
 4 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index 2ac4aedfff..bfb3c043a0 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -101,7 +101,7 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict)
 return;
 }
 
-opts = drive_def(optstr);
+opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
 if (!opts)
 return;
 
diff --git a/blockdev.c b/blockdev.c
index b35072644e..9839e234cb 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -197,17 +197,12 @@ static int drive_index_to_unit_id(BlockInterfaceType 
type, int index)
 return max_devs ? index % max_devs : index;
 }
 
-QemuOpts *drive_def(const char *optstr)
-{
-return qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
-}
-
 QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
 const char *optstr)
 {
 QemuOpts *opts;
 
-opts = drive_def(optstr);
+opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
 if (!opts) {
 return NULL;
 }
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
index 32c2d6023c..b236425c6e 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -45,14 +45,12 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo);
 void override_max_devs(BlockInterfaceType type, int max_devs);
 
 DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
-void drive_mark_claimed_by_board(void);
 void drive_check_orphaned(void);
 DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
 int drive_get_max_bus(BlockInterfaceType type);
 int drive_get_max_devs(BlockInterfaceType type);
 DriveInfo *drive_get_next(BlockInterfaceType type);
 
-QemuOpts *drive_def(const char *optstr);
 QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
 const char *optstr);
 DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type,
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 1159a64bce..fbf12f64d2 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -2884,7 +2884,9 @@ void qemu_init(int argc, char **argv, char **envp)
 break;
 }
 case QEMU_OPTION_drive:
-if (drive_def(optarg) == NULL) {
+opts = qemu_opts_parse_noisily(qemu_find_opts("drive"),
+   optarg, false);
+if (opts == NULL) {
 exit(1);
 }
 break;
-- 
2.31.1




Re: [PATCH v2] block-backend: prevent dangling BDS pointers across aio_poll()

2021-12-15 Thread Stefan Hajnoczi
On Tue, Dec 14, 2021 at 03:59:49PM +0100, Kevin Wolf wrote:
> Am 14.12.2021 um 15:35 hat Stefan Hajnoczi geschrieben:
> > The BlockBackend root child can change when aio_poll() is invoked. This
> > happens when a temporary filter node is removed upon blockjob
> > completion, for example.
> > 
> > Functions in block/block-backend.c must be aware of this when using a
> > blk_bs() pointer across aio_poll() because the BlockDriverState refcnt
> > may reach 0, resulting in a stale pointer.
> > 
> > One example is scsi_device_purge_requests(), which calls blk_drain() to
> > wait for in-flight requests to cancel. If the backup blockjob is active,
> > then the BlockBackend root child is a temporary filter BDS owned by the
> > blockjob. The blockjob can complete during bdrv_drained_begin() and the
> > last reference to the BDS is released when the temporary filter node is
> > removed. This results in a use-after-free when blk_drain() calls
> > bdrv_drained_end(bs) on the dangling pointer.
> > 
> > Explicitly hold a reference to bs across block APIs that invoke
> > aio_poll().
> > 
> > Signed-off-by: Stefan Hajnoczi 
> > ---
> > v2:
> > - Audit block/block-backend.c and fix additional cases
> > ---
> >  block/block-backend.c | 11 +++
> >  1 file changed, 11 insertions(+)
> > 
> > diff --git a/block/block-backend.c b/block/block-backend.c
> > index 12ef80ea17..a40ad7fa92 100644
> > --- a/block/block-backend.c
> > +++ b/block/block-backend.c
> > @@ -828,10 +828,12 @@ void blk_remove_bs(BlockBackend *blk)
> >  notifier_list_notify(>remove_bs_notifiers, blk);
> >  if (tgm->throttle_state) {
> >  bs = blk_bs(blk);
> > +bdrv_ref(bs);
> >  bdrv_drained_begin(bs);
> >  throttle_group_detach_aio_context(tgm);
> >  throttle_group_attach_aio_context(tgm, qemu_get_aio_context());
> >  bdrv_drained_end(bs);
> > +bdrv_unref(bs);
> >  }
> >  
> >  blk_update_root_state(blk);
> 
> This hunk is unnecessary, we still hold a reference that is only given
> up a few lines down with bdrv_root_unref_child(root).

That's not the only place where the reference can be dropped:
bdrv_drop_filter() removes the filter node from the graph.

Here is a case where it happens: block/backup.c:backup_clean() ->
bdrv_cbw_drop() -> bdrv_drop_filter() -> bdrv_replace_node_common() ->
bdrv_replace_child_commit(). After we reach this bdrv_unref() is called
a few times and all references are dropped because the node is no longer
in the graph.

This happens during blk_remove_bs() -> bdrv_drained_begin(), so the bs
pointer in the above hunk can be stale.

Stefan


signature.asc
Description: PGP signature


Re: [PATCH v2 3/4] include/sysemu/blockdev.h: move drive_add and inline drive_def

2021-12-15 Thread Emanuele Giuseppe Esposito




On 15/12/2021 11:00, Kevin Wolf wrote:

Am 15.12.2021 um 10:19 hat Emanuele Giuseppe Esposito geschrieben:



On 14/12/2021 15:35, Kevin Wolf wrote:

Am 30.11.2021 um 10:46 hat Emanuele Giuseppe Esposito geschrieben:

drive_add is only used in softmmu/vl.c, so it can be a static
function there, and drive_def is only a particular use case of
qemu_opts_parse_noisily, so it can be inlined.

Also remove drive_mark_claimed_by_board, as it is only defined
but not implemented (nor used) anywhere.

Signed-off-by: Emanuele Giuseppe Esposito 


I don't think moving drive_add() actually improves anything. Yes, you
can make it static, but in order to do that you had to introduce
block_if_name() as a new public function and you're moving an obviously
block related function to common code in vl.c.

So this part doesn't look like a net win to me. The rest of the series
looks good to me.



So are we going to drop patch 2 and 3? For me it is fine either way, and I
saw Stefan added r-b to all patches.

If we are, Kevin are you going to apply only patch 1 and 4, or do you want
me to send v3?


This patch does a bit more than just moving drive_add(). It also inlines
drive_def() and deletes drive_mark_claimed_by_board(), which are both
things that make sense to me. So this suggests a v3.

But if you think I should just apply patches 1 and 4, I'm happy to do
that, too.


You're right, I forgot about that. I will send v3, thanks.

Emanuele




Re: [PATCH-for-6.2? v2 0/3] misc: Spell QEMU all caps

2021-12-15 Thread Philippe Mathieu-Daudé
Ping?

On 11/19/21 10:16, Philippe Mathieu-Daudé wrote:
> Replace Qemu -> QEMU.
> 
> Supersedes: <2028143401.4101497-1-phi...@redhat.com>
> 
> Philippe Mathieu-Daudé (3):
>   docs: Spell QEMU all caps
>   misc: Spell QEMU all caps
>   qga: Spell QEMU all caps
> 
>  docs/devel/modules.rst |  2 +-
>  docs/devel/multi-thread-tcg.rst|  2 +-
>  docs/devel/style.rst   |  2 +-
>  docs/devel/ui.rst  |  4 ++--
>  docs/interop/nbd.txt   |  6 +++---
>  docs/interop/qcow2.txt |  8 
>  docs/multiseat.txt |  2 +-
>  docs/system/device-url-syntax.rst.inc  |  2 +-
>  docs/system/i386/sgx.rst   | 26 +-
>  docs/u2f.txt   |  2 +-
>  qapi/block-core.json   |  2 +-
>  python/qemu/machine/machine.py |  2 +-
>  qga/installer/qemu-ga.wxs  |  6 +++---
>  scripts/checkpatch.pl  |  2 +-
>  scripts/render_block_graph.py  |  2 +-
>  scripts/simplebench/bench-backup.py|  4 ++--
>  scripts/simplebench/bench_block_job.py |  2 +-
>  target/hexagon/README  |  2 +-
>  tests/guest-debug/run-test.py  |  4 ++--
>  tests/qemu-iotests/testenv.py  |  2 +-
>  20 files changed, 42 insertions(+), 42 deletions(-)
> 




Re: [PATCH-for-6.2 0/2] hw/scsi/megasas: Avoid overflowing the SGL buffer

2021-12-15 Thread Philippe Mathieu-Daudé
ping?

On 12/13/21 13:32, Philippe Mathieu-Daudé wrote:
> Too late for 6.2 now, so Cc'ing qemu-trivial (series reviewed).
> 
> On 11/19/21 21:11, Philippe Mathieu-Daudé wrote:
>> Fix issue #521 reported by Alex some months ago:
>> https://gitlab.com/qemu-project/qemu/-/issues/521
>>
>> Philippe Mathieu-Daudé (2):
>>   hw/scsi/megasas: Fails command if SGL buffer overflows
>>   tests/qtest/fuzz-megasas-test: Add test for GitLab issue #521




Re: [PATCH v5 05/31] block-backend: special comments for blk_set/get_perm due to fuse

2021-12-15 Thread Emanuele Giuseppe Esposito




On 15/12/2021 09:57, Emanuele Giuseppe Esposito wrote:



On 10/12/2021 15:38, Hanna Reitz wrote:

On 24.11.21 07:43, Emanuele Giuseppe Esposito wrote:

Fuse logic can be classified as I/O, so there is no BQL held
during its execution. And yet, it uses blk_{get/set}_perm
functions, that are classified as BQL and clearly require
the BQL lock. Since there is no easy solution for this,
add a couple of TODOs and FIXME in the relevant sections of the
code.


Well, the problem goes deeper, because we still consider them GS 
functions.  So it’s fine for the permission function 
raw_handle_perm_lock() to call bdrv_get_flags(), which is a GS 
function, and then you still get:


qemu-storage-daemon: ../block.c:6195: bdrv_get_flags: Assertion 
`qemu_in_main_thread()' failed.


Wait... Now that I read it more carefully I am confused about this. I 
don't think the above has to do with fuse, right?

Can you share the command that makes qemu-storage-daemon fail?

raw_handle_perm_lock() is currently called by these three callbacks:
.bdrv_check_perm = raw_check_perm,
.bdrv_set_perm   = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,

all three function pointers are defined as GS functions. So I don't 
understand why is this failing.




It looks like in this case making bdrv_get_flags() not a GS function 
would be feasible and might solve the problem, but I guess we should 
instead think about adding something like


if (!exp->growable && !qemu_in_main_thread()) {
 /* Changing permissions like below only works in the main thread */
 return -EPERM;
}

to fuse_do_truncate().

Ideally, to make up for this, we should probably take the RESIZE 
permission in fuse_export_create() for writable exports in iothreads.


I think I got it. I will send the RESIZE permission fix in a separate 
patch.


Thank you,
Emanuele





Re: [PATCH v2 3/4] include/sysemu/blockdev.h: move drive_add and inline drive_def

2021-12-15 Thread Kevin Wolf
Am 15.12.2021 um 10:19 hat Emanuele Giuseppe Esposito geschrieben:
> 
> 
> On 14/12/2021 15:35, Kevin Wolf wrote:
> > Am 30.11.2021 um 10:46 hat Emanuele Giuseppe Esposito geschrieben:
> > > drive_add is only used in softmmu/vl.c, so it can be a static
> > > function there, and drive_def is only a particular use case of
> > > qemu_opts_parse_noisily, so it can be inlined.
> > > 
> > > Also remove drive_mark_claimed_by_board, as it is only defined
> > > but not implemented (nor used) anywhere.
> > > 
> > > Signed-off-by: Emanuele Giuseppe Esposito 
> > 
> > I don't think moving drive_add() actually improves anything. Yes, you
> > can make it static, but in order to do that you had to introduce
> > block_if_name() as a new public function and you're moving an obviously
> > block related function to common code in vl.c.
> > 
> > So this part doesn't look like a net win to me. The rest of the series
> > looks good to me.
> > 
> 
> So are we going to drop patch 2 and 3? For me it is fine either way, and I
> saw Stefan added r-b to all patches.
> 
> If we are, Kevin are you going to apply only patch 1 and 4, or do you want
> me to send v3?

This patch does a bit more than just moving drive_add(). It also inlines
drive_def() and deletes drive_mark_claimed_by_board(), which are both
things that make sense to me. So this suggests a v3.

But if you think I should just apply patches 1 and 4, I'm happy to do
that, too.

Kevin




Re: [PATCH v2 3/4] include/sysemu/blockdev.h: move drive_add and inline drive_def

2021-12-15 Thread Emanuele Giuseppe Esposito




On 14/12/2021 15:35, Kevin Wolf wrote:

Am 30.11.2021 um 10:46 hat Emanuele Giuseppe Esposito geschrieben:

drive_add is only used in softmmu/vl.c, so it can be a static
function there, and drive_def is only a particular use case of
qemu_opts_parse_noisily, so it can be inlined.

Also remove drive_mark_claimed_by_board, as it is only defined
but not implemented (nor used) anywhere.

Signed-off-by: Emanuele Giuseppe Esposito 


I don't think moving drive_add() actually improves anything. Yes, you
can make it static, but in order to do that you had to introduce
block_if_name() as a new public function and you're moving an obviously
block related function to common code in vl.c.

So this part doesn't look like a net win to me. The rest of the series
looks good to me.



So are we going to drop patch 2 and 3? For me it is fine either way, and 
I saw Stefan added r-b to all patches.


If we are, Kevin are you going to apply only patch 1 and 4, or do you 
want me to send v3?


Thank you,
Emanuele




Re: [PATCH v5 05/31] block-backend: special comments for blk_set/get_perm due to fuse

2021-12-15 Thread Emanuele Giuseppe Esposito




On 10/12/2021 15:38, Hanna Reitz wrote:

On 24.11.21 07:43, Emanuele Giuseppe Esposito wrote:

Fuse logic can be classified as I/O, so there is no BQL held
during its execution. And yet, it uses blk_{get/set}_perm
functions, that are classified as BQL and clearly require
the BQL lock. Since there is no easy solution for this,
add a couple of TODOs and FIXME in the relevant sections of the
code.


Well, the problem goes deeper, because we still consider them GS 
functions.  So it’s fine for the permission function 
raw_handle_perm_lock() to call bdrv_get_flags(), which is a GS function, 
and then you still get:


qemu-storage-daemon: ../block.c:6195: bdrv_get_flags: Assertion 
`qemu_in_main_thread()' failed.


It looks like in this case making bdrv_get_flags() not a GS function 
would be feasible and might solve the problem, but I guess we should 
instead think about adding something like


if (!exp->growable && !qemu_in_main_thread()) {
     /* Changing permissions like below only works in the main thread */
     return -EPERM;
}

to fuse_do_truncate().

Ideally, to make up for this, we should probably take the RESIZE 
permission in fuse_export_create() for writable exports in iothreads.


I think I got it. I will send the RESIZE permission fix in a separate patch.

Thank you,
Emanuele