Re: [Qemu-devel] [RFC PATCH qemu] spapr: Stop providing RTAS blob
Patchew URL: https://patchew.org/QEMU/20190716053522.78813-1-...@ozlabs.ru/ Hi, This series failed build test on FreeBSD host. Please find the details below. === TEST SCRIPT BEGIN === #!/bin/bash # Testing script will be invoked under the git checkout with # HEAD pointing to a commit that has the patches applied on top of "base" # branch if qemu-system-x86_64 --help >/dev/null 2>&1; then QEMU=qemu-system-x86_64 elif /usr/libexec/qemu-kvm --help >/dev/null 2>&1; then QEMU=/usr/libexec/qemu-kvm else exit 1 fi make vm-build-freebsd J=21 QEMU=$QEMU exit 0 === TEST SCRIPT END === The full log is available at http://patchew.org/logs/20190716053522.78813-1-...@ozlabs.ru/testing.FreeBSD/?type=message. --- Email generated automatically by Patchew [https://patchew.org/]. Please send your feedback to patchew-de...@redhat.com
[Qemu-devel] [RFC PATCH qemu] spapr: Stop providing RTAS blob
SLOF implements one itself so let's remove it from QEMU. It is one less image and simpler setup as the RTAS blob never stays in its initial place anyway as the guest OS always decides where to put it. This totally depends on https://patchwork.ozlabs.org/patch/1132440/ , hence RFC. Signed-off-by: Alexey Kardashevskiy --- configure | 6 + Makefile| 2 +- pc-bios/spapr-rtas/Makefile | 27 - include/hw/ppc/spapr.h | 2 -- hw/ppc/spapr.c | 32 ++--- hw/ppc/spapr_rtas.c | 41 MAINTAINERS | 2 -- pc-bios/spapr-rtas.bin | Bin 20 -> 0 bytes pc-bios/spapr-rtas/spapr-rtas.S | 37 9 files changed, 4 insertions(+), 145 deletions(-) delete mode 100644 pc-bios/spapr-rtas/Makefile delete mode 100644 pc-bios/spapr-rtas.bin delete mode 100644 pc-bios/spapr-rtas/spapr-rtas.S diff --git a/configure b/configure index 4983c8b53300..a132d2eb5666 100755 --- a/configure +++ b/configure @@ -6205,9 +6205,6 @@ if { test "$cpu" = "i386" || test "$cpu" = "x86_64"; } && \ fi done fi -if test "$ARCH" = "ppc64" && test "$targetos" != "Darwin" ; then - roms="$roms spapr-rtas" -fi # Only build s390-ccw bios if we're on s390x and the compiler has -march=z900 if test "$cpu" = "s390x" ; then @@ -7919,14 +7916,13 @@ fi DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm" DIRS="$DIRS tests/fp tests/qgraph" DIRS="$DIRS docs docs/interop fsdev scsi" -DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" +DIRS="$DIRS pc-bios/optionrom pc-bios/s390-ccw" DIRS="$DIRS roms/seabios roms/vgabios" LINKS="Makefile tests/tcg/Makefile" LINKS="$LINKS tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit" LINKS="$LINKS tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile" LINKS="$LINKS tests/fp/Makefile" LINKS="$LINKS pc-bios/optionrom/Makefile pc-bios/keymaps" -LINKS="$LINKS pc-bios/spapr-rtas/Makefile" LINKS="$LINKS pc-bios/s390-ccw/Makefile" LINKS="$LINKS roms/seabios/Makefile roms/vgabios/Makefile" LINKS="$LINKS pc-bios/qemu-icon.bmp" diff --git a/Makefile b/Makefile index 1fcbaed62c76..d780f4eebceb 100644 --- a/Makefile +++ b/Makefile @@ -764,7 +764,7 @@ efi-e1000e.rom efi-vmxnet3.rom \ bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \ s390-ccw.img s390-netboot.img \ -spapr-rtas.bin slof.bin skiboot.lid \ +slof.bin skiboot.lid \ palcode-clipper \ u-boot.e500 u-boot-sam460-20100605.bin \ qemu_vga.ndrv \ diff --git a/pc-bios/spapr-rtas/Makefile b/pc-bios/spapr-rtas/Makefile deleted file mode 100644 index 4b9bb1230658.. --- a/pc-bios/spapr-rtas/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -all: build-all -# Dummy command so that make thinks it has done something - @true - -include ../../config-host.mak -include $(SRC_PATH)/rules.mak - -$(call set-vpath, $(SRC_PATH)/pc-bios/spapr-rtas) - -.PHONY : all clean build-all - -#CFLAGS += -I$(SRC_PATH) -#QEMU_CFLAGS = $(CFLAGS) - -build-all: spapr-rtas.bin - -%.o: %.S - $(call quiet-command,$(CCAS) -mbig -c -o $@ $<,"CCAS","$(TARGET_DIR)$@") - -%.img: %.o - $(call quiet-command,$(CC) -nostdlib -mbig -o $@ $<,"Building","$(TARGET_DIR)$@") - -%.bin: %.img - $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@,"Building","$(TARGET_DIR)$@") - -clean: - rm -f *.o *.d *.img *.bin *~ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 60553d32c4fa..b6640370c839 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -152,8 +152,6 @@ struct SpaprMachineState { hwaddr rma_size; int vrma_adjust; -ssize_t rtas_size; -void *rtas_blob; uint32_t fdt_size; uint32_t fdt_initial_size; void *fdt_blob; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 8783b433960c..36cd45bd78b3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -89,7 +89,6 @@ * We load our kernel at 4M, leaving space for SLOF initial image */ #define FDT_MAX_SIZE0x10 -#define RTAS_MAX_SIZE 0x1 #define RTAS_MAX_ADDR 0x8000 /* RTAS must stay below that */ #define FW_MAX_SIZE 0x40 #define FW_FILE_NAME"slof.bin" @@ -1704,8 +1703,7 @@ static void spapr_machine_reset(MachineState *machine) { SpaprMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; -uint32_t rtas_limit; -hwaddr rtas_addr, fdt_addr; +hwaddr fdt_addr; void *fdt; int rc; @@ -1783,14 +1781,10 @@ static void spapr_machine_reset(MachineState *machine) * or just below 2GB, whichever is lower, so that it can be * processed with 32-bit real mode code if necessary */ -rtas_limit =
Re: [Qemu-devel] [PATCH v2 10/11] block/backup: support bitmap sync modes for non-bitmap backups
John Snow writes: > Accept bitmaps and sync policies for the other backup modes. > This allows us to do things like create a bitmap synced to a full backup > without a transaction, or start a resumable backup process. > > Some combinations don't make sense, though: > > - NEVER policy combined with any non-BITMAP mode doesn't do anything, > because the bitmap isn't used for input or output. > It's harmless, but is almost certainly never what the user wanted. > > - sync=NONE is more questionable. It can't use on-success because this > job never completes with success anyway, and the resulting artifact > of 'always' is suspect: because we start with a full bitmap and only > copy out segments that get written to, the final output bitmap will > always be ... a fully set bitmap. > > Maybe there's contexts in which bitmaps make sense for sync=none, > but not without more severe changes to the current job, and omitting > it here doesn't prevent us from adding it later. > > Signed-off-by: John Snow > --- [...] > diff --git a/qapi/block-core.json b/qapi/block-core.json > index 5a578806c5..099e4f37b2 100644 > --- a/qapi/block-core.json > +++ b/qapi/block-core.json > @@ -1352,13 +1352,15 @@ > # @speed: the maximum speed, in bytes per second. The default is 0, > # for unlimited. > # > -# @bitmap: the name of a dirty bitmap if sync is "bitmap" or "incremental". > +# @bitmap: The name of a dirty bitmap to use. > # Must be present if sync is "bitmap" or "incremental". > +# Can be present if sync is "full" or "top". > # Must not be present otherwise. > # (Since 2.4 (drive-backup), 3.1 (blockdev-backup)) > # > # @bitmap-mode: Specifies the type of data the bitmap should contain after > -# the operation concludes. Must be present if sync is "bitmap". > +# the operation concludes. > +# Must be present if a bitmap was provided, > # Must NOT be present otherwise. (Since 4.2) > # > # @compress: true to compress data, if the target format supports it. Do you expect management applications will want to know about the presence of this patch?
[Qemu-devel] [PATCH] ppc/pnv: Warn when using -initrd and low ram
When booting with the default amount of RAM the powernv machine will load the initrd above the top of RAM and cause the Linux kernel to crash when it attempts to access the initrd: Linux/PowerPC load: Finalizing device tree... flat tree at 0x202770c0 [0.070476] nvram: Failed to find or create lnx,oops-log partition, err -28 [0.073270] nvram: Failed to initialize oops partition! [0.156302] BUG: Unable to handle kernel data access at 0xc0006000 [0.158009] Faulting instruction address: 0xc1002e5c cpu 0x0: Vector: 300 (Data Access) at [c0003d1e3870] pc: c1002e5c: unpack_to_rootfs+0xdc/0x2f0 lr: c1002df4: unpack_to_rootfs+0x74/0x2f0 sp: c0003d1e3b00 msr: 92009033 dar: c0006000 dsisr: 4000 current = 0xc0003d1c paca= 0xc132 irqmask: 0x03 irq_happened: 0x01 pid = 1, comm = swapper/0 Linux version 5.2.0-10292-g040e2e618374 (joel@voyager) (gcc version 8.3.0 (Debian 8.3.0-2)) #1 SMP Tue Jul 16 13:50:32 ACST 2019 enter ? for help [c0003d1e3bb0] c1003c90 populate_rootfs+0x84/0x1dc [c0003d1e3c40] c000f494 do_one_initcall+0x88/0x1d0 [c0003d1e3d10] c1000fc4 kernel_init_freeable+0x24c/0x250 [c0003d1e3db0] c000f7a0 kernel_init+0x1c/0x150 [c0003d1e3e20] c000b8a4 ret_from_kernel_thread+0x5c/0x78 Provide a helpful message for users so they don't go reporting bugs to kernel developers. Signed-off-by: Joel Stanley --- We could solve this in other ways, such as warn when loading the initrd outside of RAM, or load it within the known boundaries or RAM, but after hitting this myself I wanted to start the discussion. Signed-off-by: Joel Stanley --- hw/ppc/pnv.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index bd4531c82260..bbd596ab9eca 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -649,6 +649,13 @@ static void pnv_init(MachineState *machine) /* load initrd */ if (machine->initrd_filename) { +if (machine->ram_size <= (1.5 * GiB)) { +/* INITRD_LOAD_ADDR is at 1.5GB, so we require at least that much RAM + * when specifying the initrd on the command line */ +warn_report("initrd load requires > %ld MB of RAM", +INITRD_LOAD_ADDR / MiB); +} + pnv->initrd_base = INITRD_LOAD_ADDR; pnv->initrd_size = load_image_targphys(machine->initrd_filename, pnv->initrd_base, INITRD_MAX_SIZE); -- 2.20.1
[Qemu-devel] [PULL 0/3] Build system and documentation patches for 2019-07-15
The following changes since commit 5ea8ec2fcf57cb9af24ad2cf17b4d64adb03afdf: Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-07-15' into staging (2019-07-15 16:11:47 +0100) are available in the Git repository at: git://repo.or.cz/qemu/armbru.git tags/pull-build-2019-07-15 for you to fetch changes up to 32481687e1a262a9ca0083f8e938d7b0614d823b: qemu-tech: Fix dangling @menu entries (2019-07-15 21:10:29 +0200) Build system and documentation patches for 2019-07-15 Markus Armbruster (3): Makefile: Fix "make install" when "make all" needs work Makefile: Fix missing dependency of on qemu-tech.texi qemu-tech: Fix dangling @menu entries Makefile | 4 +++- qemu-tech.texi | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) -- 2.21.0
[Qemu-devel] [PULL 3/3] qemu-tech: Fix dangling @menu entries
Recent commit 2f2c4e4731 "Convert "translator internals" docs to RST, move to devel manual" and commit 282d36b5e2 "qemu-tech.texi: Remove "QEMU compared to other emulators" section" removed @node, but left their @menu entries behind. This broke building qemu-doc.info (but not qemu-doc.{html,pdf,txt}; how odd). Bury the dead @menu entries. Reported-by: Philippe Mathieu-Daudé Fixes: 2f2c4e4731449449a2b1aafcd73e4f9ae107d78b Fixes: 282d36b5e27ba86d42d0638430e439c2c257367b Signed-off-by: Markus Armbruster Message-Id: <20190715055736.15214-3-arm...@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell --- qemu-tech.texi | 3 --- 1 file changed, 3 deletions(-) diff --git a/qemu-tech.texi b/qemu-tech.texi index 3451cfaa5b..0380de77b6 100644 --- a/qemu-tech.texi +++ b/qemu-tech.texi @@ -3,10 +3,7 @@ @menu * CPU emulation:: -* Translator Internals:: -* QEMU compared to other emulators:: * Managed start up options:: -* Bibliography:: @end menu @node CPU emulation -- 2.21.0
[Qemu-devel] [PULL 2/3] Makefile: Fix missing dependency of on qemu-tech.texi
The qemu-doc.{html,info,pdf,txt} depend on qemu-doc.texi and its include files. Except qemu-tech.texi is missing. Has always been missing as far as I can see. Fix it. Signed-off-by: Markus Armbruster Message-Id: <20190715055736.15214-2-arm...@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 09b77e8a7b..f9791dcb82 100644 --- a/Makefile +++ b/Makefile @@ -1021,7 +1021,8 @@ pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ - qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ + qemu-img.texi qemu-nbd.texi qemu-options.texi \ + qemu-tech.texi qemu-option-trace.texi \ qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ qemu-monitor-info.texi docs/qemu-block-drivers.texi \ docs/qemu-cpu-models.texi docs/security.texi -- 2.21.0
[Qemu-devel] [PULL 1/3] Makefile: Fix "make install" when "make all" needs work
Until recently, target install used to recurse into target directories in its recipe: it ran make install in a for-loop. Since target install depends on target all, this trivially ensured we run the sub-make install only after completing target all. Commit 1338a4b "Makefile: Reuse all's recursion machinery for clean and install" moved the target recursion to dependencies. That's good (the commit message explains why), but I forgot to add dependencies to ensure make runs the sub-make install only after completing target all. Do that now. Fixes: 1338a4b72659ce08eacb9de0205fe16202a22d9c Reported-by: Mark Cave-Ayland Reported-by: Guenter Roeck Tested-by: Guenter Roeck Signed-off-by: Markus Armbruster Message-Id: <20190712055935.23061-1-arm...@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefano Garzarella Tested-by: Mark Cave-Ayland --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 1fcbaed62c..09b77e8a7b 100644 --- a/Makefile +++ b/Makefile @@ -522,6 +522,7 @@ $(ROM_DIRS_RULES): recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS)) recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS)) recurse-install: $(addsuffix /install, $(TARGET_DIRS)) +$(addsuffix /install, $(TARGET_DIRS)): all $(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc config-host.h $(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<,"RC","version.o") -- 2.21.0
[Qemu-devel] [PATCH] ppc: Improve SMT experience with TCG accel
It's not possible to specify the number of threads of a guest when running QEMU/TCG. Today, users can have setups like: ... -accel tcg,thread=multi -smp 8,threads=1,cores=8 ... or ... -accel tcg,thread=multi -smp 8,sockets=2,cores=4,threads=1 ... However, the following is not possible: ... -accel tcg,thread=multi -smp 16,threads=4,cores=2,sockets=2 ... qemu-system-ppc64: TCG cannot support more than 1 thread/core on a pseries machine The reason is due to how SMT is implemented since Power8. This patch implements a very basic simulation of the msgsndp instruction, using ext interrupt instead of doorbells. The result is a better user experience, allowing them to play with SMT modes. However, it doesn't relate with MTTCG threads in any way. Results: ... -accel tcg,thread=multi -smp 16,threads=4,cores=2,sockets=2 ... root@ubuntu:~# ppc64_cpu --smt SMT=4 root@ubuntu:~# ppc64_cpu --info Core 0:0*1*2*3* Core 1:4*5*6*7* Core 2:8*9* 10* 11* Core 3: 12* 13* 14* 15* root@ubuntu:~# ppc64_cpu --smt=2 root@ubuntu:~# ppc64_cpu --info Core 0:0*1*2 3 Core 1:4*5*6 7 Core 2:8*9* 1011 Core 3: 12* 13* 1415 root@ubuntu:~# ppc64_cpu --smt=off root@ubuntu:~# ppc64_cpu --info Core 0:0*1 2 3 Core 1:4*5 6 7 Core 2:8*91011 Core 3: 12* 131415 root@ubuntu:~# ppc64_cpu --smt SMT is off root@ubuntu:~# lscpu Architecture: ppc64le Byte Order: Little Endian CPU(s): 16 On-line CPU(s) list: 0,4,8,12 Off-line CPU(s) list: 1-3,5-7,9-11,13-15 Thread(s) per core: 1 Core(s) per socket: 2 Socket(s):2 NUMA node(s): 1 Model:2.0 (pvr 004e 1200) Model name: POWER9 (architected), altivec supported Hypervisor vendor:KVM Virtualization type: para L1d cache:32K L1i cache:32K NUMA node0 CPU(s):0,4,8,12 root@ubuntu:~# ppc64_cpu --smt=4 root@ubuntu:~# lscpu Architecture:ppc64le Byte Order: Little Endian CPU(s): 16 On-line CPU(s) list: 0-15 Thread(s) per core: 4 Core(s) per socket: 2 Socket(s): 2 NUMA node(s):1 Model: 2.0 (pvr 004e 1200) Model name: POWER9 (architected), altivec supported Hypervisor vendor: KVM Virtualization type: para L1d cache: 32K L1i cache: 32K NUMA node0 CPU(s): 0-15 Note: it's also possible to simulate SMT in TCG single threaded mode. Signed-off-by: Jose Ricardo Ziviani --- hw/ppc/spapr.c | 5 - target/ppc/excp_helper.c | 24 target/ppc/helper.h | 1 + target/ppc/translate.c | 11 +++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 8783b43396..3a864dfc7d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2572,11 +2572,6 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) int ret; unsigned int smp_threads = ms->smp.threads; -if (!kvm_enabled() && (smp_threads > 1)) { -error_setg(_err, "TCG cannot support more than 1 thread/core " - "on a pseries machine"); -goto out; -} if (!is_power_of_2(smp_threads)) { error_setg(_err, "Cannot support %d threads/core on a pseries " "machine because it must be a power of 2", smp_threads); diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 50b004d00d..ac5d196641 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1231,6 +1231,30 @@ static int book3s_dbell2irq(target_ulong rb) return msg == DBELL_TYPE_DBELL_SERVER ? PPC_INTERRUPT_HDOORBELL : -1; } +void helper_msgsndp(target_ulong rb) +{ +CPUState *cs; +int irq = rb & DBELL_TYPE_MASK; +int thread_id = rb & 0x3f; + +if (irq != DBELL_TYPE_DBELL_SERVER) { +return; +} + +qemu_mutex_lock_iothread(); +CPU_FOREACH(cs) { +PowerPCCPU *cpu = POWERPC_CPU(cs); + +if (cpu->vcpu_id == thread_id) { +continue; +} + +cpu->env.pending_interrupts |= 1 << PPC_INTERRUPT_EXT; +cpu_interrupt(cs, CPU_INTERRUPT_HARD); +} +qemu_mutex_unlock_iothread(); +} + void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) { int irq = book3s_dbell2irq(rb); diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 380c9b1e2a..eadd08324b 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -630,6 +630,7 @@ DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_1(msgsnd, void, tl) +DEF_HELPER_1(msgsndp, void, tl) DEF_HELPER_2(msgclr, void, env, tl) DEF_HELPER_1(book3s_msgsnd, void, tl) DEF_HELPER_2(book3s_msgclr, void, env, tl) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index
[Qemu-devel] [PATCH 1/1] virtio-net: check guest header length is valid
virtio-net checks that the "out" sg is longer than the guest header, but this check can be skipped if has_net_hdr is 0. Also perform this check if host_hdr_len != guest_hdr_len Signed-off-by: Alexander Oleinik --- hw/net/virtio-net.c | 13 - 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index b9e1cd71cf..46d715b4f5 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -2064,7 +2064,18 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) */ assert(n->host_hdr_len <= n->guest_hdr_len); if (n->host_hdr_len != n->guest_hdr_len) { -unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg), +unsigned sg_num; + +if (!n->has_vnet_hdr) { +if (iov_to_buf(out_sg, out_num, 0, , n->guest_hdr_len) < +n->guest_hdr_len) { +virtio_error(vdev, "virtio-net header incorrect"); +virtqueue_detach_element(q->tx_vq, elem, 0); +g_free(elem); +return -EINVAL; +} +} +sg_num = iov_copy(sg, ARRAY_SIZE(sg), out_sg, out_num, 0, n->host_hdr_len); sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num, -- 2.20.1
[Qemu-devel] [PATCH 0/1] Add check for header length in virtio-net-tx
While fuzzing the virtio-net tx vq, I ran into an assertion failure due to iov_copy offsets larger than the total iov size. Though there is a check to cover this, it does not execute when !n->has_vnet_hdr. This patch tries to fix this. The call stack for the assertion failure: #8 in __assert_fail (libc.so.6+0x300f1) #9 in iov_copy iov.c:266:5 #10 in virtio_net_flush_tx virtio-net.c:2073:23 #11 in virtio_net_tx_bh virtio-net.c:2197:11 #12 in aio_bh_poll async.c:118:13 #13 in aio_dispatch aio-posix.c:460:5 #14 in aio_ctx_dispatch async.c:261:5 #15 in g_main_context_dispatch (libglib-2.0.so.0+0x4df2d) #16 in glib_pollfds_poll main-loop.c:213:9 #17 in os_host_main_loop_wait main-loop.c:236 #18 in main_loop_wait main-loop.c:512 #19 in virtio_net_tx_fuzz virtio-net-fuzz.c:160:3 Thanks -Alex Alexander Oleinik (1): virtio-net: check guest header length is valid hw/net/virtio-net.c | 13 - 1 file changed, 12 insertions(+), 1 deletion(-) -- 2.20.1
Re: [Qemu-devel] [PATCH v4 0/5] spapr: implement dispatch and suspend calls
Patchew URL: https://patchew.org/QEMU/20190716024726.17864-1-npig...@gmail.com/ Hi, This series seems to have some coding style problems. See output below for more information: Subject: [Qemu-devel] [PATCH v4 0/5] spapr: implement dispatch and suspend calls Message-id: 20190716024726.17864-1-npig...@gmail.com Type: series === TEST SCRIPT BEGIN === #!/bin/bash git rev-parse base > /dev/null || exit 0 git config --local diff.renamelimit 0 git config --local diff.renames True git config --local diff.algorithm histogram ./scripts/checkpatch.pl --mailback base.. === TEST SCRIPT END === Switched to a new branch 'test' c432dea spapr: Implement ibm,suspend-me 09bae6b spapr: Implement H_JOIN 031bd3f spapr: Implement H_CONFER a3d8bc2 spapr: Implement H_PROD ed23e67 spapr: Implement dispatch counter and prod bit on tcg === OUTPUT BEGIN === 1/5 Checking commit ed23e6798f18 (spapr: Implement dispatch counter and prod bit on tcg) ERROR: line over 90 characters #33: FILE: hw/ppc/spapr.c:4316: +stl_be_phys(cs->as, spapr_cpu->vpa_addr + VPA_DISPATCH_COUNTER, spapr_cpu->dispatch_counter); total: 1 errors, 0 warnings, 131 lines checked Patch 1/5 has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS. 2/5 Checking commit a3d8bc2510cc (spapr: Implement H_PROD) 3/5 Checking commit 031bd3f6986b (spapr: Implement H_CONFER) 4/5 Checking commit 09bae6bec0c1 (spapr: Implement H_JOIN) ERROR: braces {} are necessary for all arms of this statement #55: FILE: hw/ppc/spapr_hcall.c:1092: +if (c == cpu) [...] ERROR: code indent should never use tabs #58: FILE: hw/ppc/spapr_hcall.c:1095: +^I/* Don't have a way to indicate joined, so use halted && MSR[EE]=0 */$ total: 2 errors, 0 warnings, 63 lines checked Patch 4/5 has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS. 5/5 Checking commit c432deae5480 (spapr: Implement ibm,suspend-me) ERROR: braces {} are necessary for all arms of this statement #93: FILE: hw/ppc/spapr_rtas.c:234: +if (c == cpu) [...] ERROR: code indent should never use tabs #96: FILE: hw/ppc/spapr_rtas.c:237: +^I/* See h_join */$ WARNING: Block comments use a leading /* on a separate line #128: FILE: include/hw/ppc/spapr.h:174: +/* Machine has been suspended, so the next machine_reset should not WARNING: Block comments use a trailing */ on a separate line #129: FILE: include/hw/ppc/spapr.h:175: + * reset state, but just return and allow execution to resume. */ total: 2 errors, 2 warnings, 108 lines checked Patch 5/5 has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS. === OUTPUT END === Test command exited with code: 1 The full log is available at http://patchew.org/logs/20190716024726.17864-1-npig...@gmail.com/testing.checkpatch/?type=message. --- Email generated automatically by Patchew [https://patchew.org/]. Please send your feedback to patchew-de...@redhat.com
[Qemu-devel] [PATCH v4 4/5] spapr: Implement H_JOIN
This has been useful to modify and test the Linux pseries suspend code but it requires modification to the guest to call it (due to being gated by other unimplemented features). It is not otherwise used by Linux yet, but work is slowly progressing there. Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 1 + hw/ppc/spapr_hcall.c | 44 2 files changed, 45 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 87b11e2484..5c54e1cb9a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1066,6 +1066,7 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) add_str(hypertas, "hcall-tce"); add_str(hypertas, "hcall-vio"); add_str(hypertas, "hcall-splpar"); +add_str(hypertas, "hcall-join"); add_str(hypertas, "hcall-bulk"); add_str(hypertas, "hcall-set-mode"); add_str(hypertas, "hcall-sprg0"); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 28d58113be..52847a7047 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1069,6 +1069,47 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, return H_SUCCESS; } +static target_ulong h_join(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ +CPUPPCState *env = >env; +CPUState *cs; +SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); +bool last_unjoined = true; + +if (env->msr & (1ULL << MSR_EE)) { +return H_BAD_MODE; +} + +if (spapr_cpu->prod) { +spapr_cpu->prod = false; +return H_SUCCESS; +} + +CPU_FOREACH(cs) { +PowerPCCPU *c = POWERPC_CPU(cs); +CPUPPCState *e = >env; +if (c == cpu) +continue; + + /* Don't have a way to indicate joined, so use halted && MSR[EE]=0 */ +if (!cs->halted || (e->msr & (1ULL << MSR_EE))) { +last_unjoined = false; +break; +} +} +if (last_unjoined) { +return H_CONTINUE; +} + +cs = CPU(cpu); +cs->halted = 1; +cs->exception_index = EXCP_HALTED; +cs->exit_request = 1; + +return H_SUCCESS; +} + static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { @@ -1959,6 +2000,9 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_CONFER, h_confer); spapr_register_hypercall(H_PROD, h_prod); +/* hcall-join */ +spapr_register_hypercall(H_JOIN, h_join); + spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset); /* processor register resource access h-calls */ -- 2.20.1
[Qemu-devel] [PATCH v4 5/5] spapr: Implement ibm,suspend-me
This has been useful to modify and test the Linux pseries suspend code but it requires modification to the guest to call it (due to being gated by other unimplemented features). It is not otherwise used by Linux yet, but work is slowly progressing there. This allows a (lightly modified) guest kernel to suspend with `echo mem > /sys/power/state` and be resumed with system_wakeup monitor command. Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 26 ++ hw/ppc/spapr_rtas.c| 32 include/hw/ppc/spapr.h | 7 ++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 5c54e1cb9a..b85d41bb1e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1710,6 +1710,11 @@ static void spapr_machine_reset(MachineState *machine) void *fdt; int rc; +if (spapr->suspend_reset) { +spapr->suspend_reset = false; +return; +} + spapr_caps_apply(spapr); first_ppc_cpu = POWERPC_CPU(first_cpu); @@ -2721,6 +2726,23 @@ static PCIHostState *spapr_create_default_phb(void) return PCI_HOST_BRIDGE(dev); } +static Notifier wakeup; +static void spapr_notify_wakeup(Notifier *notifier, void *data) +{ +WakeupReason *reason = data; + +switch (*reason) { +case QEMU_WAKEUP_REASON_RTC: +break; +case QEMU_WAKEUP_REASON_PMTIMER: +break; +case QEMU_WAKEUP_REASON_OTHER: +break; +default: +break; +} +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { @@ -3078,6 +3100,10 @@ static void spapr_machine_init(MachineState *machine) qemu_register_boot_set(spapr_boot_set, spapr); +wakeup.notify = spapr_notify_wakeup; +qemu_register_wakeup_notifier(); +qemu_register_wakeup_support(); + if (kvm_enabled()) { /* to stop and start vmclock */ qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change, diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index a618a2ac0f..60a007ec38 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -216,6 +216,36 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, qemu_cpu_kick(cs); } +static void rtas_ibm_suspend_me(PowerPCCPU *cpu, SpaprMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ +CPUState *cs; + +if (nargs != 0 || nret != 1) { +rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +return; +} + +CPU_FOREACH(cs) { +PowerPCCPU *c = POWERPC_CPU(cs); +CPUPPCState *e = >env; +if (c == cpu) +continue; + + /* See h_join */ +if (!cs->halted || (e->msr & (1ULL << MSR_EE))) { +rtas_st(rets, 0, H_MULTI_THREADS_ACTIVE); +return; +} +} + +spapr->suspend_reset = true; +qemu_system_suspend_request(); +rtas_st(rets, 0, RTAS_OUT_SUCCESS); +} + static inline int sysparm_st(target_ulong addr, target_ulong len, const void *val, uint16_t vallen) { @@ -483,6 +513,8 @@ static void core_rtas_register_types(void) rtas_query_cpu_stopped_state); spapr_rtas_register(RTAS_START_CPU, "start-cpu", rtas_start_cpu); spapr_rtas_register(RTAS_STOP_SELF, "stop-self", rtas_stop_self); +spapr_rtas_register(RTAS_IBM_SUSPEND_ME, "ibm,suspend-me", +rtas_ibm_suspend_me); spapr_rtas_register(RTAS_IBM_GET_SYSTEM_PARAMETER, "ibm,get-system-parameter", rtas_ibm_get_system_parameter); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 5d36eec9d0..df0b0c15da 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -171,6 +171,10 @@ struct SpaprMachineState { bool use_hotplug_event_source; SpaprEventSource *event_sources; +/* Machine has been suspended, so the next machine_reset should not + * reset state, but just return and allow execution to resume. */ +bool suspend_reset; + /* ibm,client-architecture-support option negotiation */ bool cas_reboot; bool cas_legacy_guest_workaround; @@ -631,8 +635,9 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, #define RTAS_IBM_CREATE_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x27) #define RTAS_IBM_REMOVE_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x28) #define RTAS_IBM_RESET_PE_DMA_WINDOW(RTAS_TOKEN_BASE + 0x29) +#define RTAS_IBM_SUSPEND_ME (RTAS_TOKEN_BASE + 0x2A) -#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2A) +#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2B) /* RTAS ibm,get-system-parameter token values */ #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20 -- 2.20.1
[Qemu-devel] [PATCH v4 3/5] spapr: Implement H_CONFER
This does not do directed yielding and is not quite as strict as PAPR specifies in terms of precise dispatch behaviour. This generally will mean suboptimal performance, rather than guest misbehaviour. Linux does not rely on exact dispatch behaviour. Signed-off-by: Nicholas Piggin --- hw/ppc/spapr_hcall.c | 48 1 file changed, 48 insertions(+) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 8b208ab259..28d58113be 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1069,6 +1069,53 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, return H_SUCCESS; } +static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ +target_long target = args[0]; +uint32_t dispatch = args[1]; +PowerPCCPU *target_cpu = spapr_find_cpu(target); +CPUState *target_cs = CPU(target_cpu); +CPUState *cs = CPU(cpu); +SpaprCpuState *spapr_cpu; + +/* + * This does not do a targeted yield or confer, but check the parameter + * anyway. -1 means confer to all/any other CPUs. + */ +if (target != -1 && !target_cs) { +return H_PARAMETER; +} + +spapr_cpu = spapr_cpu_state(target_cpu); + +/* + * PAPR specifies waiting until proded in this case, without dispatch + * counter check. + */ +if (cpu == target_cpu) { +if (spapr_cpu->prod) { +spapr_cpu->prod = false; +return H_SUCCESS; +} + +cs->halted = 1; +cs->exception_index = EXCP_HALTED; +cs->exit_request = 1; + +return H_SUCCESS; +} + +if (spapr_cpu->dispatch_counter != dispatch || (dispatch & 1) == 0) { +return H_SUCCESS; +} + +cs->exception_index = EXCP_YIELD; +cpu_loop_exit(cs); + +return H_SUCCESS; +} + static target_ulong h_prod(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { @@ -1909,6 +1956,7 @@ static void hypercall_register_types(void) /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); +spapr_register_hypercall(H_CONFER, h_confer); spapr_register_hypercall(H_PROD, h_prod); spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset); -- 2.20.1
[Qemu-devel] [PATCH v4 0/5] spapr: implement dispatch and suspend calls
This series follows on from the previous that added H_PROD and H_CONFER, but I've now aimed to make it conform better to PAPR. It's still not completely there (as explained in comments), but it's better than before and actually better matches KVM that does implement the prod bit and dispatch counter. The first 3 patches implement these splpar hcalls for tcg, as KVM implements its own H_PROD, H_CONFER, H_CEDE, and dispatch management. These would be nice to merge as they make qemu behave more like KVM and PowerVM with these calls. The last 2 patches implement some parts of the guest suspend APIs I've been using to test Linux modifications to the pseries (and generic kernel) suspend/hibernate code, but they are not really useful to a Linux guest (yet) due to other missing bits. I have some Linux code I'll try to gradually upstream to work around the missing bits and make this suspend on QEMU "work", which is at least useful for testing without having PowerVM. Thanks, Nick Nicholas Piggin (5): spapr: Implement dispatch counter and prod bit on tcg spapr: Implement H_PROD spapr: Implement H_CONFER spapr: Implement H_JOIN spapr: Implement ibm,suspend-me hw/ppc/spapr.c | 52 + hw/ppc/spapr_cpu_core.c | 5 +- hw/ppc/spapr_hcall.c| 126 ++-- hw/ppc/spapr_rtas.c | 32 include/hw/ppc/spapr.h | 14 +++- include/hw/ppc/spapr_cpu_core.h | 2 + target/ppc/cpu.h| 2 + target/ppc/translate_init.inc.c | 25 +++ 8 files changed, 251 insertions(+), 7 deletions(-) -- 2.20.1
[Qemu-devel] [PATCH v4 1/5] spapr: Implement dispatch counter and prod bit on tcg
Implement cpu_exec_enter/exit on ppc which calls into new methods of the same name in PPCVirtualHypervisorClass. These are used by spapr to implement these splpar elements, used in subsequent changes. Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 25 + hw/ppc/spapr_cpu_core.c | 5 - hw/ppc/spapr_hcall.c| 5 - include/hw/ppc/spapr.h | 7 +++ include/hw/ppc/spapr_cpu_core.h | 2 ++ target/ppc/cpu.h| 2 ++ target/ppc/translate_init.inc.c | 25 + 7 files changed, 65 insertions(+), 6 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 821f0d4a49..87b11e2484 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4302,6 +4302,29 @@ PowerPCCPU *spapr_find_cpu(int vcpu_id) return NULL; } +static void spapr_cpu_exec_enter(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) +{ +SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + +/* These are only called by TCG, KVM maintains dispatch state */ + +spapr_cpu->prod = false; +spapr_cpu->dispatch_counter++; +assert((spapr_cpu->dispatch_counter & 1) == 0); +if (spapr_cpu->vpa_addr) { +CPUState *cs = CPU(cpu); +stl_be_phys(cs->as, spapr_cpu->vpa_addr + VPA_DISPATCH_COUNTER, spapr_cpu->dispatch_counter); +} +} + +static void spapr_cpu_exec_exit(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) +{ +SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + +spapr_cpu->dispatch_counter++; +assert((spapr_cpu->dispatch_counter & 1) == 1); +} + static void spapr_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -4358,6 +4381,8 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) vhc->hpte_set_r = spapr_hpte_set_r; vhc->get_pate = spapr_get_pate; vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr; +vhc->cpu_exec_enter = spapr_cpu_exec_enter; +vhc->cpu_exec_exit = spapr_cpu_exec_exit; xic->ics_get = spapr_ics_get; xic->ics_resend = spapr_ics_resend; xic->icp_get = spapr_icp_get; diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 5621fb9a3d..fb2ed9e95d 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -261,6 +261,7 @@ error: static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) { SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc); +SpaprCpuState *spapr_cpu; CPUCore *cc = CPU_CORE(sc); Object *obj; char *id; @@ -287,7 +288,9 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) goto err; } -cpu->machine_data = g_new0(SpaprCpuState, 1); +spapr_cpu = g_new0(SpaprCpuState, 1); +spapr_cpu->dispatch_counter = 1; +cpu->machine_data = spapr_cpu; object_unref(obj); return cpu; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 6808d4cda8..e615881ac4 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -874,11 +874,6 @@ unmap_out: #define FLAGS_DEREGISTER_DTL 0xc000ULL #define FLAGS_DEREGISTER_SLBSHADOW 0xe000ULL -#define VPA_MIN_SIZE 640 -#define VPA_SIZE_OFFSET0x4 -#define VPA_SHARED_PROC_OFFSET 0x9 -#define VPA_SHARED_PROC_VAL0x2 - static target_ulong register_vpa(PowerPCCPU *cpu, target_ulong vpa) { CPUState *cs = CPU(cpu); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 60553d32c4..5d36eec9d0 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -525,6 +525,13 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, target_ulong *args); +/* Virtual Processor Area structure constants */ +#define VPA_MIN_SIZE 640 +#define VPA_SIZE_OFFSET0x4 +#define VPA_SHARED_PROC_OFFSET 0x9 +#define VPA_SHARED_PROC_VAL0x2 +#define VPA_DISPATCH_COUNTER 0x100 + /* ibm,set-eeh-option */ #define RTAS_EEH_DISABLE 0 #define RTAS_EEH_ENABLE 1 diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index f9645a7290..3032dfa7ee 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -46,6 +46,8 @@ typedef struct SpaprCpuState { uint64_t vpa_addr; uint64_t slb_shadow_addr, slb_shadow_size; uint64_t dtl_addr, dtl_size; +uint32_t dispatch_counter; +bool prod; struct ICPState *icp; struct XiveTCTX *tctx; } SpaprCpuState; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c9beba2a5c..78d6504acb 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1224,6 +1224,8 @@ struct PPCVirtualHypervisorClass { void (*hpte_set_r)(PPCVirtualHypervisor *vhyp, hwaddr ptex, uint64_t pte1); void (*get_pate)(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry); target_ulong
[Qemu-devel] [PATCH v4 2/5] spapr: Implement H_PROD
H_PROD is added, and H_CEDE is modified to test the prod bit according to PAPR. Signed-off-by: Nicholas Piggin --- hw/ppc/spapr_hcall.c | 29 + 1 file changed, 29 insertions(+) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index e615881ac4..8b208ab259 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1050,14 +1050,41 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, { CPUPPCState *env = >env; CPUState *cs = CPU(cpu); +SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); env->msr |= (1ULL << MSR_EE); hreg_compute_hflags(env); + +if (spapr_cpu->prod) { +spapr_cpu->prod = false; +return H_SUCCESS; +} + if (!cpu_has_work(cs)) { cs->halted = 1; cs->exception_index = EXCP_HLT; cs->exit_request = 1; } + +return H_SUCCESS; +} + +static target_ulong h_prod(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ +target_long target = args[0]; +CPUState *cs; +SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + +cs = CPU(spapr_find_cpu(target)); +if (!cs) { +return H_PARAMETER; +} + +spapr_cpu->prod = true; +cs->halted = 0; +qemu_cpu_kick(cs); + return H_SUCCESS; } @@ -1882,6 +1909,8 @@ static void hypercall_register_types(void) /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); +spapr_register_hypercall(H_PROD, h_prod); + spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset); /* processor register resource access h-calls */ -- 2.20.1
Re: [Qemu-devel] [Qemu-ppc] [PULL 41/44] spapr: change default interrupt mode to 'dual'
On Mon, Jul 15, 2019 at 12:19:03PM +0200, Cédric Le Goater wrote: > On 11/07/2019 03:26, David Gibson wrote: > > On Wed, Jul 10, 2019 at 06:26:09PM +0200, Laurent Vivier wrote: > >> On 29/05/2019 08:50, David Gibson wrote: > >>> From: Cédric Le Goater > >>> > >>> Now that XIVE support is complete (QEMU emulated and KVM devices), > >>> change the pseries machine to advertise both interrupt modes: XICS > >>> (P7/P8) and XIVE (P9). > >>> > >>> The machine default interrupt modes depends on the version. Current > >>> settings are: > >>> > >>> pseries default interrupt mode > >>> > >>> 4.1 dual > >>> 4.0 xics > >>> 3.1 xics > >>> 3.0 legacy xics (different IRQ number space layout) > >>> > >>> Signed-off-by: Cédric Le Goater > >>> Message-Id: <20190522074016.10521-3-...@kaod.org> > >>> Reviewed-by: Greg Kurz > >>> Signed-off-by: David Gibson > >>> --- > >>> hw/ppc/spapr.c | 3 ++- > >>> 1 file changed, 2 insertions(+), 1 deletion(-) > >>> > >>> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c > >>> index 39e698e9b0..4fd16b43f0 100644 > >>> --- a/hw/ppc/spapr.c > >>> +++ b/hw/ppc/spapr.c > >>> @@ -4352,7 +4352,7 @@ static void spapr_machine_class_init(ObjectClass > >>> *oc, void *data) > >>> smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON; > >>> smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF; > >>> spapr_caps_add_properties(smc, _abort); > >>> -smc->irq = _irq_xics; > >>> +smc->irq = _irq_dual; > >>> smc->dr_phb_enabled = true; > >>> } > >>> > >>> @@ -4430,6 +4430,7 @@ static void > >>> spapr_machine_4_0_class_options(MachineClass *mc) > >>> spapr_machine_4_1_class_options(mc); > >>> compat_props_add(mc->compat_props, hw_compat_4_0, hw_compat_4_0_len); > >>> smc->phb_placement = phb_placement_4_0; > >>> +smc->irq = _irq_xics; > >>> } > >>> > >>> DEFINE_SPAPR_MACHINE(4_0, "4.0", false); > >>> > >> > >> This patch breaks the '-no-reboot' parameter (I think the "dual" mode > >> breaks the -no-reboot parameter) > >> > >> After grub loads the kernel and starts it, the kernel aborts: > >> > >> OF stdout device is: /vdevice/vty@7100 > >> Preparing to boot Linux version 4.18.0-112.el8.ppc64le > >> (mockbu...@ppc-061.build.eng.bos.redhat.com) (gcc version 8.3.1 20190507 > >> (Red Hat 8.3.1-4) (GCC)) #1 SMP Fri Jul 5 11:21:28 UTC 2019 > >> Detected machine type: 0101 > >> command line: BOOT_IMAGE=/vmlinuz-4.18.0-112.el8.ppc64le > >> root=/dev/mapper/rhel_ibm--p8--kvm--03--guest--02-root ro > >> crashkernel=auto rd.lvm.lv=rhel_ibm-p8-kvm-03-guest-02/root > >> rd.lvm.lv=rhel_ibm-p8-kvm-03-guest-02/swap > >> Max number of cores passed to firmware: 256 (NR_CPUS = 2048) > >> Calling ibm,client-architecture-support...[lvivier@localhost ~]$ > >> > >> I bisected to this patch, and then after I understood the problem is > >> with the -no-reboot parameter as the machine is reset by the CAS > >> negotiation... and the -no-reboot prevents this reset. > >> > >> I don't know if it's a real problem or not. > > > > Ah, bother. I didn't think of the interaction between the CAS reboot > > and -no-reboot. I guess that's more reason to work out a way to do > > the xics/xive switch without a full reset. People were already not > > thrilled with the extra reboots here. > > QEMU builds a device tree depending on the interrupt mode negotiated > at CAS time. Can we dynamically add/remove nodes ? I believe we can make essentially arbitrary change to the device tree as part of the CAS process without a reboot. Re-doing this is kind of awkward with the current structure, but it's probably what we're going to want. -- David Gibson| I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson signature.asc Description: PGP signature
[Qemu-devel] Fwd: virtio_scsi_ctx_check failed when detach virtio_scsi disk
Forwarded Message Subject:virtio_scsi_ctx_check failed when detach virtio_scsi disk Date: Mon, 15 Jul 2019 23:34:24 +0800 From: l00284672 To: kw...@redhat.com, be...@igalia.com, Stefan Hajnoczi , Paolo Bonzini CC: lizhen...@huawei.com I found a problem that virtio_scsi_ctx_check failed when detaching virtio_scsi disk. The bt is below: (gdb) bt #0 0xb02e1bd0 in raise () from /lib64/libc.so.6 #1 0xb02e2f7c in abort () from /lib64/libc.so.6 #2 0xb02db124 in __assert_fail_base () from /lib64/libc.so.6 #3 0xb02db1a4 in __assert_fail () from /lib64/libc.so.6 #4 0x004eb9a8 invirtio_scsi_ctx_check (d=d@entry=0xc70d790, s=, s=) at /Images/lzg/code/710/qemu-2.8.1/hw/scsi/virtio-scsi.c:243 #5 0x004ec87c in virtio_scsi_handle_cmd_req_prepare (s=s@entry=0xd27a7a0, req=req@entry=0xafc4b90) at /Images/lzg/code/710/qemu-2.8.1/hw/scsi/virtio-scsi.c:553 #6 0x004ecc20 in virtio_scsi_handle_cmd_vq (s=0xd27a7a0, vq=0xd283410) at /Images/lzg/code/710/qemu-2.8.1/hw/scsi/virtio-scsi.c:588 #7 0x004eda20 in virtio_scsi_data_plane_handle_cmd (vdev=0x0, vq=0xae7a6f98) at /Images/lzg/code/710/qemu-2.8.1/hw/scsi/virtio-scsi-dataplane.c:57 #8 0x00877254 in aio_dispatch (ctx=0xac61010) at util/aio-posix.c:323 #9 0x008773ec in aio_poll (ctx=0xac61010, blocking=true) at util/aio-posix.c:472 #10 0x005cd7cc in iothread_run (opaque=0xac5e4b0) at iothread.c:49 #11 0x0087a8b8 in qemu_thread_start (args=0xac61360) at util/qemu-thread-posix.c:495 #12 0x008a04e8 in thread_entry_for_hotfix (pthread_cb=0x0) at uvp/hotpatch/qemu_hotpatch_helper.c:579 #13 0xb041c8bc in start_thread () from /lib64/libpthread.so.0 #14 0xb0382f8c in thread_start () from /lib64/libc.so.6 assert(blk_get_aio_context(d->conf.blk) == s->ctx) failed. I think this patch (https://git.qemu.org/?p=qemu.git;a=commitdiff;h=a6f230c8d13a7ff3a0c7f1097412f44bfd9eff0b) introduce this problem. commit a6f230c8d13a7ff3a0c7f1097412f44bfd9eff0b move blockbackend back to main AioContext on unplug. It set the AioContext of SCSIDevice to the main AioContex, but s->ctx is still the iothread AioContext. Is this a bug? <>
[Qemu-devel] [PATCH v2] qapi: add dirty-bitmaps to query-named-block-nodes result
From: Vladimir Sementsov-Ogievskiy Let's add a possibility to query dirty-bitmaps not only on root nodes. It is useful when dealing both with snapshots and incremental backups. Signed-off-by: Vladimir Sementsov-Ogievskiy [Added deprecation and feature flag information. --js] Signed-off-by: John Snow --- block/qapi.c | 5 + qapi/block-core.json | 14 +- qemu-deprecated.texi | 12 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/block/qapi.c b/block/qapi.c index 917435f022..15f1030264 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -79,6 +79,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->backing_file = g_strdup(bs->backing_file); } +if (!QLIST_EMPTY(>dirty_bitmaps)) { +info->has_dirty_bitmaps = true; +info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); +} + info->detect_zeroes = bs->detect_zeroes; if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { diff --git a/qapi/block-core.json b/qapi/block-core.json index 0d43d4f37c..0d67dd245c 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -360,6 +360,16 @@ # @write_threshold: configured write threshold for the device. # 0 if disabled. (Since 2.3) # +# @dirty-bitmaps: dirty bitmaps information (only present if node +# has one or more dirty bitmaps) (Since 4.2) +# +# Features: +# @node-dirty-bitmaps: Signals the capability to return dirty bitmap information +# per-node instead of per-drive. If this flag is present, +# dirty-bitmaps should not be read from the BlockInfo +# structure, the top-level data for query-block. +# (Since 4.2) +# # Since: 0.14.0 # ## @@ -378,7 +388,8 @@ '*bps_wr_max_length': 'int', '*iops_max_length': 'int', '*iops_rd_max_length': 'int', '*iops_wr_max_length': 'int', '*iops_size': 'int', '*group': 'str', 'cache': 'BlockdevCacheInfo', -'write_threshold': 'int' } } +'write_threshold': 'int', '*dirty-bitmaps': ['BlockDirtyInfo'] }, + 'features': [ { 'name': 'node-dirty-bitmaps' } ] } ## # @BlockDeviceIoStatus: @@ -656,6 +667,7 @@ # # @dirty-bitmaps: dirty bitmaps information (only present if the # driver has one or more dirty bitmaps) (Since 2.0) +# Deprecated in 4.2; see BlockDirtyInfo instead. # # @io-status: @BlockDeviceIoStatus. Only present if the device # supports it and the VM is configured to stop on errors diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi index c90b08d553..bc4e5ac1d7 100644 --- a/qemu-deprecated.texi +++ b/qemu-deprecated.texi @@ -134,6 +134,18 @@ The ``status'' field of the ``BlockDirtyInfo'' structure, returned by the query-block command is deprecated. Two new boolean fields, ``recording'' and ``busy'' effectively replace it. +@subsection query-block result field dirty-bitmaps (Since 4.2) + +The ``dirty-bitmaps`` field of the ``BlockInfo`` structure, returned by +the query-block command is itself now deprecated. The ``dirty-bitmaps`` +field of the ``BlockDeviceInfo`` struct should be used instead, which is the +type of the ``inserted`` field in query-block replies, as well as the +type of array items in query-named-block-nodes. + +In the absence of bitmaps on either structure, management APIs may use the +presence of the ``node-dirty-bitmaps`` feature flag on the ``BlockDeviceInfo`` +structure to know where to anticipate bitmap data when present. + @subsection query-cpus (since 2.12.0) The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. -- 2.21.0
[Qemu-devel] [PATCH] migration: consolidate time info into populate_time_info
Consolidate time information fill up into its function for better readability. Signed-off-by: Wei Yang --- migration/migration.c | 40 ++-- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 47fe22d327..18ef933105 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -822,6 +822,25 @@ bool migration_is_setup_or_active(int state) } } +static void populate_time_info(MigrationInfo *info, MigrationState *s) +{ +info->has_status = true; +info->has_setup_time = true; +info->setup_time = s->setup_time; +if (s->state == MIGRATION_STATUS_COMPLETED) { +info->has_total_time = true; +info->total_time = s->total_time; +info->has_downtime = true; +info->downtime = s->downtime; +} else { +info->has_total_time = true; +info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - + s->start_time; +info->has_expected_downtime = true; +info->expected_downtime = s->expected_downtime; +} +} + static void populate_ram_info(MigrationInfo *info, MigrationState *s) { info->has_ram = true; @@ -907,16 +926,8 @@ static void fill_source_migration_info(MigrationInfo *info) case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_POSTCOPY_PAUSED: case MIGRATION_STATUS_POSTCOPY_RECOVER: - /* TODO add some postcopy stats */ -info->has_status = true; -info->has_total_time = true; -info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -- s->start_time; -info->has_expected_downtime = true; -info->expected_downtime = s->expected_downtime; -info->has_setup_time = true; -info->setup_time = s->setup_time; - +/* TODO add some postcopy stats */ +populate_time_info(info, s); populate_ram_info(info, s); populate_disk_info(info); break; @@ -925,14 +936,7 @@ static void fill_source_migration_info(MigrationInfo *info) /* TODO: display COLO specific information (checkpoint info etc.) */ break; case MIGRATION_STATUS_COMPLETED: -info->has_status = true; -info->has_total_time = true; -info->total_time = s->total_time; -info->has_downtime = true; -info->downtime = s->downtime; -info->has_setup_time = true; -info->setup_time = s->setup_time; - +populate_time_info(info, s); populate_ram_info(info, s); break; case MIGRATION_STATUS_FAILED: -- 2.17.1
Re: [Qemu-devel] [PATCH] qapi: add dirty-bitmaps to query-named-block-nodes result
On 6/5/19 8:46 AM, Markus Armbruster wrote: > John Snow writes: > >> On 5/31/19 10:55 AM, Eric Blake wrote: >>> On 5/30/19 11:26 AM, John Snow wrote: On 5/30/19 10:39 AM, Vladimir Sementsov-Ogievskiy wrote: > Let's add a possibility to query dirty-bitmaps not only on root nodes. > It is useful when dealing both with snapshots and incremental backups. > >>> > +++ b/block/qapi.c > @@ -78,6 +78,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend > *blk, > info->backing_file = g_strdup(bs->backing_file); > } > > +if (!QLIST_EMPTY(>dirty_bitmaps)) { > +info->has_dirty_bitmaps = true; > +info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); > +} > + > info->detect_zeroes = bs->detect_zeroes; > > if (blk && > blk_get_public(blk)->throttle_group_member.throttle_state) { > So query-block uses bdrv_query_info, which calls bdrv_block_device_info, so we'll duplicate the bitmap output when doing the old-fashioned block query, but that's probably harmless overall. >>> >>> We already know that none of our existing query- interfaces are sane >>> (either too little information, or too much). Duplication starts to >>> push an interface towards too much (it takes processor time to bundle up >>> the extra JSON, especially if the other end is not going to care if it >>> was present). I know Kevin still has somewhere on his to-do list the >>> implementation of a saner query- command for the information we really >>> want (about each block, without redundant information, and where we >>> don't repeat information in a nested manner, but where we also don't >>> omit information that would otherwise require multiple existing query- >>> to reconstruct). >>> We can continue to support the output in both places, or we could opt to deprecate the older interface; I think this is one of the last chances we'd get to do so before libvirt and wider adoption. I think that's probably Eric's choice. >>> >>> If you want to try to deprecate the old location, introspection at least >>> works to allow libvirt to know which place to look for it on a given >>> qemu. If you don't think deprecation is necessary, the duplication is >>> probably tolerable for now (as ideally we'd be deprecating ALL of our >>> not-quite-perfect query- block interfaces in favor of whatever sane >>> interface Kevin comes up with). >>> >> >> It sounds like it's probably the right move to deprecate the entire >> legacy interface, but still... If you have 20 or 30 bitmaps on a root >> node, you will see 40 or 60 entries. >> >> What's the smart way to deprecate it? We're not adding new flags or >> showing new arguments or anything. There might not be bitmaps, so you >> can't rely on that field being present or absent. >> >> Recommendations? > > Kevin's "[PATCH v4 0/6] file-posix: Add dynamic-auto-read-only QAPI > feature" adds "feature flags" to the QAPI schema language, limited to > struct types, because that's what he needs. They're visible in > introspection. I intend to complete his work, so we can tack > "deprecated" feature flags to pretty much anything > > Could that address your need? > Hi Markus, digging this up again. In brief, we are displaying bitmap info in the "wrong" part of the query result (attached to drive instead of node) and would like to change it. I'd like to avoid reporting bitmaps in both locations permanently, so if we have a plan to deprecate reporting bitmaps in the old location, I will tolerate the duplicated output temporarily. Keeping in mind the bitmap fields are optional (so they can be absent from both the new and old locations), what plan can we implement? Perhaps I can add a feature flag "has-node-bitmaps" for 4.2. Then, for the next three versions, I will report bitmaps from both locations. Then, in 5.2+ I will remove the old location. A client knows it can find bitmaps (if there are any) in the new location if the feature flag is set. Otherwise, it should look in the old location. I think I've convinced myself that this is correct, so correct me if I am wrong. --js
[Qemu-devel] [PATCH v2 05/11] iotests/257: test API failures
Signed-off-by: John Snow --- tests/qemu-iotests/257 | 67 ++ tests/qemu-iotests/257.out | 85 ++ 2 files changed, 152 insertions(+) diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index aaa8f59504..53ab31c92e 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -447,10 +447,77 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): compare_images(img_path, fbackup2) log('') +def test_backup_api(): +""" +Test malformed and prohibited invocations of the backup API. +""" +with iotests.FilePaths(['img', 'bsync1']) as \ + (img_path, backup_path), \ + iotests.VM() as vm: + +log("\n=== API failure tests ===\n") +log('--- Preparing image & VM ---\n') +drive0 = Drive(img_path, vm=vm) +drive0.img_create(iotests.imgfmt, SIZE) +vm.add_device("{},id=scsi0".format(iotests.get_virtio_scsi_device())) +vm.launch() + +file_config = { +'driver': 'file', +'filename': drive0.path +} + +vm.qmp_log('blockdev-add', + filters=[iotests.filter_qmp_testfiles], + node_name="drive0", + driver=drive0.fmt, + file=file_config) +drive0.node = 'drive0' +drive0.device = 'device0' +vm.qmp_log("device_add", id=drive0.device, + drive=drive0.name, driver="scsi-hd") +log('') + +target0 = Drive(backup_path, vm=vm) +target0.create_target("backup_target", drive0.fmt, drive0.size) +log('') + +vm.qmp_log("block-dirty-bitmap-add", node=drive0.name, + name="bitmap0", granularity=GRANULARITY) +log('') + +log('-- Testing invalid QMP commands --\n') + +error_cases = { +'incremental': { +None:['on-success', 'always', 'never', None], +'bitmap404': ['on-success', 'always', 'never', None], +'bitmap0': ['always', 'never'] +}, +'bitmap': { +None:['on-success', 'always', 'never', None], +'bitmap404': ['on-success', 'always', 'never', None], +'bitmap0': [None], +}, +} + +# Dicts, as always, are not stably-ordered prior to 3.7, so use tuples: +for sync_mode in ('incremental', 'bitmap'): +log("-- Sync mode {:s} tests --\n".format(sync_mode)) +for bitmap in (None, 'bitmap404', 'bitmap0'): +for policy in error_cases[sync_mode][bitmap]: +blockdev_backup(drive0.vm, drive0.name, "backup_target", +sync_mode, job_id='api_job', +bitmap=bitmap, bitmap_mode=policy) +log('') + + def main(): for bsync_mode in ("never", "on-success", "always"): for failure in ("simulated", "intermediate", None): test_bitmap_sync(bsync_mode, "bitmap", failure) +test_backup_api() + if __name__ == '__main__': iotests.script_main(main, supported_fmts=['qcow2']) diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out index 0abc96acd3..43f2e0f9c9 100644 --- a/tests/qemu-iotests/257.out +++ b/tests/qemu-iotests/257.out @@ -2245,3 +2245,88 @@ qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fbackup1" ==> Identical, OK qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fbackup2" ==> Identical, OK! qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! + +=== API failure tests === + +--- Preparing image & VM --- + +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} +{"return": {}} +{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0"}} +{"return": {}} + +{} +{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}} +{"return": {}} +{} +{} +{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}} +{"return": {}} +{} + +{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}} +{"return": {}} + +-- Testing invalid QMP commands -- + +-- Sync mode incremental tests -- + +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}} + +{"execute": "blockdev-backup", "arguments": {"bitmap-mode": "always", "device": "drive0", "job-id": "api_job", "sync": "incremental", "target": "backup_target"}} +{"error": {"class": "GenericError", "desc": "must provide a valid bitmap name for 'incremental' sync mode"}} + +{"execute":
[Qemu-devel] [PATCH v2 06/11] block/backup: improve sync=bitmap work estimates
When making backups based on bitmaps, the work estimate can be more accurate. Update iotests to reflect the new strategy. TOP work estimates are broken, but do not get worse with this commit. That issue is addressed in the following commits instead. Signed-off-by: John Snow --- block/backup.c | 8 +++- tests/qemu-iotests/256.out | 4 ++-- tests/qemu-iotests/257.out | 36 ++-- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/block/backup.c b/block/backup.c index a64b768e24..22fafbb80f 100644 --- a/block/backup.c +++ b/block/backup.c @@ -449,9 +449,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) NULL, true); assert(ret); -/* TODO job_progress_set_remaining() would make more sense */ -job_progress_update(>common.job, -job->len - bdrv_get_dirty_count(job->copy_bitmap)); +job_progress_set_remaining(>common.job, + bdrv_get_dirty_count(job->copy_bitmap)); } static int coroutine_fn backup_run(Job *job, Error **errp) @@ -463,12 +462,11 @@ static int coroutine_fn backup_run(Job *job, Error **errp) QLIST_INIT(>inflight_reqs); qemu_co_rwlock_init(>flush_rwlock); -job_progress_set_remaining(job, s->len); - if (s->sync_mode == MIRROR_SYNC_MODE_BITMAP) { backup_incremental_init_copy_bitmap(s); } else { bdrv_set_dirty_bitmap(s->copy_bitmap, 0, s->len); +job_progress_set_remaining(job, s->len); } s->before_write.notify = backup_before_write_notify; diff --git a/tests/qemu-iotests/256.out b/tests/qemu-iotests/256.out index eec38614ec..f18ecb0f91 100644 --- a/tests/qemu-iotests/256.out +++ b/tests/qemu-iotests/256.out @@ -113,7 +113,7 @@ { "return": {} } -{"data": {"device": "j2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"data": {"device": "j3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "j2", "len": 0, "offset": 0, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "j3", "len": 0, "offset": 0, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} --- Done --- diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out index 43f2e0f9c9..811b1b11f1 100644 --- a/tests/qemu-iotests/257.out +++ b/tests/qemu-iotests/257.out @@ -150,7 +150,7 @@ expecting 7 dirty sectors; have 7. OK! {"execute": "job-cancel", "arguments": {"id": "backup_1"}} {"return": {}} {"data": {"id": "backup_1", "type": "backup"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"data": {"device": "backup_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "backup_1", "len": 393216, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { "device0": [ @@ -228,7 +228,7 @@ expecting 15 dirty sectors; have 15. OK! {"execute": "job-finalize", "arguments": {"id": "backup_2"}} {"return": {}} {"data": {"id": "backup_2", "type": "backup"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"data": {"device": "backup_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "backup_2", "len": 983040, "offset": 983040, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} { "bitmaps": { "device0": [ @@ -367,7 +367,7 @@ expecting 6 dirty sectors; have 6. OK! {"execute": "blockdev-backup", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "job-id": "backup_1", "sync": "bitmap", "target": "backup_target_1"}} {"return": {}} {"data": {"action": "report", "device": "backup_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"data": {"device": "backup_1", "error": "Input/output error", "len": 67108864, "offset": 66781184, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "backup_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED",
[Qemu-devel] [PATCH v2 11/11] iotests/257: test traditional sync modes
Signed-off-by: John Snow --- tests/qemu-iotests/257 | 41 +- tests/qemu-iotests/257.out | 3089 2 files changed, 3128 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index 53ab31c92e..c2a72c577a 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -283,6 +283,12 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): Bitmaps are always synchronized, regardless of failure. (Partial images must be kept.) +:param msync_mode: The mirror sync mode to use for the first backup. + Can be any one of: +- bitmap: Backups based on bitmap manifest. +- full: Full backups. +- top:Full backups of the top layer only. + :param failure: Is the (optional) failure mode, and can be any of: - None: No failure. Test the normative path. Default. - simulated:Cancel the job right before it completes. @@ -393,7 +399,7 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): # group 1 gets cleared first, then group two gets written. if ((bsync_mode == 'on-success' and not failure) or (bsync_mode == 'always')): -ebitmap.clear_group(1) +ebitmap.clear() ebitmap.dirty_group(2) vm.run_job(job, auto_dismiss=True, auto_finalize=False, @@ -404,8 +410,19 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): log('') if bsync_mode == 'always' and failure == 'intermediate': +# TOP treats anything allocated as dirty, expect to see: +if msync_mode == 'top': +ebitmap.dirty_group(0) + # We manage to copy one sector (one bit) before the error. ebitmap.clear_bit(ebitmap.first_bit) + +# Full returns all bits set except what was copied/skipped +if msync_mode == 'full': +fail_bit = ebitmap.first_bit +ebitmap.clear() +ebitmap.dirty_bits(range(fail_bit, SIZE // GRANULARITY)) + ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0')) # 2 - Writes and Reference Backup @@ -499,10 +516,25 @@ def test_backup_api(): 'bitmap404': ['on-success', 'always', 'never', None], 'bitmap0': [None], }, +'full': { +None:['on-success', 'always', 'never'], +'bitmap404': ['on-success', 'always', 'never', None], +'bitmap0': ['never', None], +}, +'top': { +None:['on-success', 'always', 'never'], +'bitmap404': ['on-success', 'always', 'never', None], +'bitmap0': ['never', None], +}, +'none': { +None:['on-success', 'always', 'never'], +'bitmap404': ['on-success', 'always', 'never', None], +'bitmap0': ['on-success', 'always', 'never', None], +} } # Dicts, as always, are not stably-ordered prior to 3.7, so use tuples: -for sync_mode in ('incremental', 'bitmap'): +for sync_mode in ('incremental', 'bitmap', 'full', 'top', 'none'): log("-- Sync mode {:s} tests --\n".format(sync_mode)) for bitmap in (None, 'bitmap404', 'bitmap0'): for policy in error_cases[sync_mode][bitmap]: @@ -517,6 +549,11 @@ def main(): for failure in ("simulated", "intermediate", None): test_bitmap_sync(bsync_mode, "bitmap", failure) +for sync_mode in ('full', 'top'): +for bsync_mode in ('on-success', 'always'): +for failure in ('simulated', 'intermediate', None): +test_bitmap_sync(bsync_mode, sync_mode, failure) + test_backup_api() if __name__ == '__main__': diff --git a/tests/qemu-iotests/257.out b/tests/qemu-iotests/257.out index 811b1b11f1..abcd9044a1 100644 --- a/tests/qemu-iotests/257.out +++ b/tests/qemu-iotests/257.out @@ -2246,6 +2246,3002 @@ qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fbackup2" ==> Identical, OK qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fbackup2" ==> Identical, OK! +=== Mode full; Bitmap Sync on-success with simulated failure === + +--- Preparing image & VM --- + +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}} +{"return": {}} +{"execute": "device_add", "arguments": {"drive": "drive0", "driver": "scsi-hd", "id": "device0", "share-rw": true}} +{"return": {}} + +--- Write #0 --- + +write -P0x49 0x000 0x1 +{"return": ""} +write -P0x6c 0x010 0x1 +{"return": ""} +write -P0x6f 0x200 0x1 +{"return": ""} +write -P0x76 0x3ff 0x1 +{"return":
[Qemu-devel] [PATCH v2 08/11] block/backup: add backup_is_cluster_allocated
Modify the existing bdrv_is_unallocated_range to utilize the pnum return from bdrv_is_allocated; optionally returning a full number of clusters that share the same allocation status. This will be used to carefully toggle bits in the bitmap for sync=top initialization in the following commits. Signed-off-by: John Snow --- block/backup.c | 62 +++--- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/block/backup.c b/block/backup.c index c88a70fe10..b407d57954 100644 --- a/block/backup.c +++ b/block/backup.c @@ -185,6 +185,48 @@ static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, return nbytes; } +/* + * Check if the cluster starting at offset is allocated or not. + * return via pnum the number of contiguous clusters sharing this allocation. + */ +static int backup_is_cluster_allocated(BackupBlockJob *s, int64_t offset, + int64_t *pnum) +{ +BlockDriverState *bs = blk_bs(s->common.blk); +int64_t count, total_count = 0; +int64_t bytes = s->len - offset; +int ret; + +assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); + +while (true) { +ret = bdrv_is_allocated(bs, offset, bytes, ); +if (ret < 0) { +return ret; +} + +total_count += count; + +if (ret || count == 0) { +/* + * ret: partial segment(s) are considered allocated. + * otherwise: unallocated tail is treated as an entire segment. + */ +*pnum = DIV_ROUND_UP(total_count, s->cluster_size); +return ret; +} + +/* Unallocated segment(s) with uncertain following segment(s) */ +if (total_count >= s->cluster_size) { +*pnum = total_count / s->cluster_size; +return 0; +} + +offset += count; +bytes -= count; +} +} + static int coroutine_fn backup_do_cow(BackupBlockJob *job, int64_t offset, uint64_t bytes, bool *error_is_read, @@ -388,34 +430,18 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job) return false; } -static bool bdrv_is_unallocated_range(BlockDriverState *bs, - int64_t offset, int64_t bytes) -{ -int64_t end = offset + bytes; - -while (offset < end && !bdrv_is_allocated(bs, offset, bytes, )) { -if (bytes == 0) { -return true; -} -offset += bytes; -bytes = end - offset; -} - -return offset >= end; -} - static int coroutine_fn backup_loop(BackupBlockJob *job) { bool error_is_read; int64_t offset; BdrvDirtyBitmapIter *bdbi; -BlockDriverState *bs = blk_bs(job->common.blk); int ret = 0; +int64_t dummy; bdbi = bdrv_dirty_iter_new(job->copy_bitmap); while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) { if (job->sync_mode == MIRROR_SYNC_MODE_TOP && -bdrv_is_unallocated_range(bs, offset, job->cluster_size)) +!backup_is_cluster_allocated(job, offset, )) { bdrv_reset_dirty_bitmap(job->copy_bitmap, offset, job->cluster_size); -- 2.21.0
[Qemu-devel] [PATCH v2 07/11] block/backup: centralize copy_bitmap initialization
Just a few housekeeping changes that keeps the following commit easier to read; perform the initial copy_bitmap initialization in one place. Signed-off-by: John Snow --- block/backup.c | 29 +++-- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/block/backup.c b/block/backup.c index 22fafbb80f..c88a70fe10 100644 --- a/block/backup.c +++ b/block/backup.c @@ -441,16 +441,22 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) return ret; } -/* init copy_bitmap from sync_bitmap */ -static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) +static void backup_init_copy_bitmap(BackupBlockJob *job) { -bool ret = bdrv_dirty_bitmap_merge_internal(job->copy_bitmap, -job->sync_bitmap, -NULL, true); -assert(ret); +bool ret; +uint64_t estimate; -job_progress_set_remaining(>common.job, - bdrv_get_dirty_count(job->copy_bitmap)); +if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) { +ret = bdrv_dirty_bitmap_merge_internal(job->copy_bitmap, + job->sync_bitmap, + NULL, true); +assert(ret); +} else { +bdrv_set_dirty_bitmap(job->copy_bitmap, 0, job->len); +} + +estimate = bdrv_get_dirty_count(job->copy_bitmap); +job_progress_set_remaining(>common.job, estimate); } static int coroutine_fn backup_run(Job *job, Error **errp) @@ -462,12 +468,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp) QLIST_INIT(>inflight_reqs); qemu_co_rwlock_init(>flush_rwlock); -if (s->sync_mode == MIRROR_SYNC_MODE_BITMAP) { -backup_incremental_init_copy_bitmap(s); -} else { -bdrv_set_dirty_bitmap(s->copy_bitmap, 0, s->len); -job_progress_set_remaining(job, s->len); -} +backup_init_copy_bitmap(s); s->before_write.notify = backup_before_write_notify; bdrv_add_before_write_notifier(bs, >before_write); -- 2.21.0
[Qemu-devel] [PATCH v2 03/11] iotests/257: Refactor backup helpers
This test needs support for non-bitmap backups and missing or unspecified bitmap sync modes, so rewrite the helpers to be a little more generic. Signed-off-by: John Snow --- tests/qemu-iotests/257 | 56 ++- tests/qemu-iotests/257.out | 192 ++--- 2 files changed, 128 insertions(+), 120 deletions(-) diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index bc66ea03b2..aaa8f59504 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -207,31 +207,37 @@ def get_bitmap(bitmaps, drivename, name, recording=None): return bitmap return None +def blockdev_backup(vm, device, target, sync, **kwargs): +# Strip any arguments explicitly nulled by the caller: +kwargs = {key: val for key, val in kwargs.items() if val is not None} +result = vm.qmp_log('blockdev-backup', +device=device, +target=target, +sync=sync, +**kwargs) +return result + +def blockdev_backup_mktarget(drive, target_id, filepath, sync, **kwargs): +target_drive = Drive(filepath, vm=drive.vm) +target_drive.create_target(target_id, drive.fmt, drive.size) +blockdev_backup(drive.vm, drive.name, target_id, sync, **kwargs) + def reference_backup(drive, n, filepath): log("--- Reference Backup #{:d} ---\n".format(n)) target_id = "ref_target_{:d}".format(n) job_id = "ref_backup_{:d}".format(n) -target_drive = Drive(filepath, vm=drive.vm) - -target_drive.create_target(target_id, drive.fmt, drive.size) -drive.vm.qmp_log("blockdev-backup", - job_id=job_id, device=drive.name, - target=target_id, sync="full") +blockdev_backup_mktarget(drive, target_id, filepath, "full", + job_id=job_id) drive.vm.run_job(job_id, auto_dismiss=True) log('') -def bitmap_backup(drive, n, filepath, bitmap, bitmap_mode): -log("--- Bitmap Backup #{:d} ---\n".format(n)) -target_id = "bitmap_target_{:d}".format(n) -job_id = "bitmap_backup_{:d}".format(n) -target_drive = Drive(filepath, vm=drive.vm) - -target_drive.create_target(target_id, drive.fmt, drive.size) -drive.vm.qmp_log("blockdev-backup", job_id=job_id, device=drive.name, - target=target_id, sync="bitmap", - bitmap_mode=bitmap_mode, - bitmap=bitmap, - auto_finalize=False) +def backup(drive, n, filepath, sync, **kwargs): +log("--- Test Backup #{:d} ---\n".format(n)) +target_id = "backup_target_{:d}".format(n) +job_id = "backup_{:d}".format(n) +kwargs.setdefault('auto-finalize', False) +blockdev_backup_mktarget(drive, target_id, filepath, sync, + job_id=job_id, **kwargs) return job_id def perform_writes(drive, n): @@ -263,7 +269,7 @@ def compare_images(image, reference, baseimg=None, expected_match=True): "OK!" if ret == expected_ret else "ERROR!"), filters=[iotests.filter_testfiles]) -def test_bitmap_sync(bsync_mode, failure=None): +def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): """ Test bitmap backup routines. @@ -291,7 +297,7 @@ def test_bitmap_sync(bsync_mode, failure=None): fbackup0, fbackup1, fbackup2), \ iotests.VM() as vm: -mode = "Bitmap Sync Mode {:s}".format(bsync_mode) +mode = "Mode {:s}; Bitmap Sync {:s}".format(msync_mode, bsync_mode) preposition = "with" if failure else "without" cond = "{:s} {:s}".format(preposition, "{:s} failure".format(failure) if failure @@ -362,12 +368,13 @@ def test_bitmap_sync(bsync_mode, failure=None): ebitmap.compare(bitmap) reference_backup(drive0, 1, fbackup1) -# 1 - Bitmap Backup (Optional induced failure) +# 1 - Test Backup (w/ Optional induced failure) if failure == 'intermediate': # Activate blkdebug induced failure for second-to-next read log(vm.hmp_qemu_io(drive0.name, 'flush')) log('') -job = bitmap_backup(drive0, 1, bsync1, "bitmap0", bsync_mode) +job = backup(drive0, 1, bsync1, msync_mode, + bitmap="bitmap0", bitmap_mode=bsync_mode) def _callback(): """Issue writes while the job is open to test bitmap divergence.""" @@ -408,7 +415,8 @@ def test_bitmap_sync(bsync_mode, failure=None): reference_backup(drive0, 2, fbackup2) # 2 - Bitmap Backup (In failure modes, this is a recovery.) -job = bitmap_backup(drive0, 2, bsync2, "bitmap0", bsync_mode) +job = backup(drive0, 2, bsync2, "bitmap", + bitmap="bitmap0", bitmap_mode=bsync_mode) vm.run_job(job, auto_dismiss=True, auto_finalize=False) bitmaps = query_bitmaps(vm)
[Qemu-devel] [PATCH v2 04/11] block/backup: hoist bitmap check into QMP interface
This is nicer to do in the unified QMP interface that we have now, because it lets us use the right terminology back at the user. Signed-off-by: John Snow Reviewed-by: Max Reitz --- block/backup.c | 13 - blockdev.c | 10 ++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/block/backup.c b/block/backup.c index e2729cf6fa..a64b768e24 100644 --- a/block/backup.c +++ b/block/backup.c @@ -566,6 +566,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, assert(bs); assert(target); +/* QMP interface protects us from these cases */ +assert(sync_mode != MIRROR_SYNC_MODE_INCREMENTAL); +assert(sync_bitmap || sync_mode != MIRROR_SYNC_MODE_BITMAP); + if (bs == target) { error_setg(errp, "Source and target cannot be the same"); return NULL; @@ -597,16 +601,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } -/* QMP interface should have handled translating this to bitmap mode */ -assert(sync_mode != MIRROR_SYNC_MODE_INCREMENTAL); - if (sync_mode == MIRROR_SYNC_MODE_BITMAP) { -if (!sync_bitmap) { -error_setg(errp, "must provide a valid bitmap name for " - "'%s' sync mode", MirrorSyncMode_str(sync_mode)); -return NULL; -} - /* If we need to write to this bitmap, check that we can: */ if (bitmap_mode != BITMAP_SYNC_MODE_NEVER && bdrv_dirty_bitmap_check(sync_bitmap, BDRV_BITMAP_DEFAULT, errp)) { diff --git a/blockdev.c b/blockdev.c index 020d566c1b..3c76c85cb5 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3527,6 +3527,16 @@ static BlockJob *do_backup_common(BackupCommon *backup, return NULL; } +if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) || +(backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) { +/* done before desugaring 'incremental' to print the right message */ +if (!backup->has_bitmap) { +error_setg(errp, "must provide a valid bitmap name for " + "'%s' sync mode", MirrorSyncMode_str(backup->sync)); +return NULL; +} +} + if (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL) { if (backup->has_bitmap_mode && backup->bitmap_mode != BITMAP_SYNC_MODE_ON_SUCCESS) { -- 2.21.0
[Qemu-devel] [PATCH v2 09/11] block/backup: teach TOP to never copy unallocated regions
Presently, If sync=TOP is selected, we mark the entire bitmap as dirty. In the write notifier handler, we dutifully copy out such regions. Fix this in three parts: 1. Mark the bitmap as being initialized before the first yield. 2. After the first yield but before the backup loop, interrogate the allocation status asynchronously and initialize the bitmap. 3. Teach the write notifier to interrogate allocation status if it is invoked during bitmap initialization. As an effect of this patch, the job progress for TOP backups now behaves like this: - total progress starts at bdrv_length. - As allocation status is interrogated, total progress decreases. - As blocks are copied, current progress increases. Taken together, the floor and ceiling move to meet each other. Signed-off-by: John Snow --- block/backup.c | 78 -- block/trace-events | 1 + 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/block/backup.c b/block/backup.c index b407d57954..e28fd23f6a 100644 --- a/block/backup.c +++ b/block/backup.c @@ -58,6 +58,7 @@ typedef struct BackupBlockJob { int64_t copy_range_size; bool serialize_target_writes; +bool initializing_bitmap; } BackupBlockJob; static const BlockJobDriver backup_job_driver; @@ -227,6 +228,35 @@ static int backup_is_cluster_allocated(BackupBlockJob *s, int64_t offset, } } +/** + * Reset bits in copy_bitmap starting at offset if they represent unallocated + * data in the image. May reset subsequent contiguous bits. + * @return 0 when the cluster at @offset was unallocated, + * 1 otherwise, and -ret on error. + */ +static int64_t backup_bitmap_reset_unallocated(BackupBlockJob *s, + int64_t offset, int64_t *count) +{ +int ret; +int64_t clusters, bytes, estimate; + +ret = backup_is_cluster_allocated(s, offset, ); +if (ret < 0) { +return ret; +} + +bytes = clusters * s->cluster_size; + +if (!ret) { +bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); +estimate = bdrv_get_dirty_count(s->copy_bitmap); +job_progress_set_remaining(>common.job, estimate); +} + +*count = bytes; +return ret; +} + static int coroutine_fn backup_do_cow(BackupBlockJob *job, int64_t offset, uint64_t bytes, bool *error_is_read, @@ -236,6 +266,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, int ret = 0; int64_t start, end; /* bytes */ void *bounce_buffer = NULL; +int64_t skip_bytes; qemu_co_rwlock_rdlock(>flush_rwlock); @@ -254,6 +285,15 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, continue; /* already copied */ } +if (job->initializing_bitmap) { +ret = backup_bitmap_reset_unallocated(job, start, _bytes); +if (ret == 0) { +trace_backup_do_cow_skip_range(job, start, skip_bytes); +start += skip_bytes; +continue; +} +} + trace_backup_do_cow_process(job, start); if (job->use_copy_range) { @@ -436,18 +476,9 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) int64_t offset; BdrvDirtyBitmapIter *bdbi; int ret = 0; -int64_t dummy; bdbi = bdrv_dirty_iter_new(job->copy_bitmap); while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) { -if (job->sync_mode == MIRROR_SYNC_MODE_TOP && -!backup_is_cluster_allocated(job, offset, )) -{ -bdrv_reset_dirty_bitmap(job->copy_bitmap, offset, -job->cluster_size); -continue; -} - do { if (yield_and_check(job)) { goto out; @@ -478,6 +509,13 @@ static void backup_init_copy_bitmap(BackupBlockJob *job) NULL, true); assert(ret); } else { +if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { +/* + * We can't hog the coroutine to initialize this thoroughly. + * Set a flag and resume work when we are able to yield safely. + */ +job->initializing_bitmap = true; +} bdrv_set_dirty_bitmap(job->copy_bitmap, 0, job->len); } @@ -499,6 +537,26 @@ static int coroutine_fn backup_run(Job *job, Error **errp) s->before_write.notify = backup_before_write_notify; bdrv_add_before_write_notifier(bs, >before_write); +if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { +int64_t offset = 0; +int64_t count; + +for (offset = 0; offset < s->len; ) { +if (yield_and_check(s)) { +ret = -ECANCELED; +goto out; +} + +ret = backup_bitmap_reset_unallocated(s, offset, ); +if (ret < 0) { +goto out; +
[Qemu-devel] [PATCH v2 01/11] iotests/257: add Pattern class
Just kidding, this is easier to manage with a full class instead of a namedtuple. Signed-off-by: John Snow --- tests/qemu-iotests/257 | 58 +++--- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index 3952683749..02f9ae0649 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -19,7 +19,6 @@ # # owner=js...@redhat.com -from collections import namedtuple import math import os @@ -29,10 +28,18 @@ from iotests import log, qemu_img SIZE = 64 * 1024 * 1024 GRANULARITY = 64 * 1024 -Pattern = namedtuple('Pattern', ['byte', 'offset', 'size']) -def mkpattern(byte, offset, size=GRANULARITY): -"""Constructor for Pattern() with default size""" -return Pattern(byte, offset, size) + +class Pattern: +def __init__(self, byte, offset, size=GRANULARITY): +self.byte = byte +self.offset = offset +self.size = size + +def bits(self, granularity): +lower = self.offset // granularity +upper = (self.offset + self.size - 1) // granularity +return set(range(lower, upper + 1)) + class PatternGroup: """Grouping of Pattern objects. Initialize with an iterable of Patterns.""" @@ -43,40 +50,39 @@ class PatternGroup: """Calculate the unique bits dirtied by this pattern grouping""" res = set() for pattern in self.patterns: -lower = pattern.offset // granularity -upper = (pattern.offset + pattern.size - 1) // granularity -res = res | set(range(lower, upper + 1)) +res |= pattern.bits(granularity) return res + GROUPS = [ PatternGroup([ # Batch 0: 4 clusters -mkpattern('0x49', 0x000), -mkpattern('0x6c', 0x010), # 1M -mkpattern('0x6f', 0x200), # 32M -mkpattern('0x76', 0x3ff)]), # 64M - 64K +Pattern('0x49', 0x000), +Pattern('0x6c', 0x010), # 1M +Pattern('0x6f', 0x200), # 32M +Pattern('0x76', 0x3ff)]), # 64M - 64K PatternGroup([ # Batch 1: 6 clusters (3 new) -mkpattern('0x65', 0x000), # Full overwrite -mkpattern('0x77', 0x00f8000), # Partial-left (1M-32K) -mkpattern('0x72', 0x2008000), # Partial-right (32M+32K) -mkpattern('0x69', 0x3fe)]), # Adjacent-left (64M - 128K) +Pattern('0x65', 0x000), # Full overwrite +Pattern('0x77', 0x00f8000), # Partial-left (1M-32K) +Pattern('0x72', 0x2008000), # Partial-right (32M+32K) +Pattern('0x69', 0x3fe)]), # Adjacent-left (64M - 128K) PatternGroup([ # Batch 2: 7 clusters (3 new) -mkpattern('0x74', 0x001), # Adjacent-right -mkpattern('0x69', 0x00e8000), # Partial-left (1M-96K) -mkpattern('0x6e', 0x2018000), # Partial-right (32M+96K) -mkpattern('0x67', 0x3fe, - 2*GRANULARITY)]), # Overwrite [(64M-128K)-64M) +Pattern('0x74', 0x001), # Adjacent-right +Pattern('0x69', 0x00e8000), # Partial-left (1M-96K) +Pattern('0x6e', 0x2018000), # Partial-right (32M+96K) +Pattern('0x67', 0x3fe, +2*GRANULARITY)]), # Overwrite [(64M-128K)-64M) PatternGroup([ # Batch 3: 8 clusters (5 new) # Carefully chosen such that nothing re-dirties the one cluster # that copies out successfully before failure in Group #1. -mkpattern('0xaa', 0x001, - 3*GRANULARITY), # Overwrite and 2x Adjacent-right -mkpattern('0xbb', 0x00d8000), # Partial-left (1M-160K) -mkpattern('0xcc', 0x2028000), # Partial-right (32M+160K) -mkpattern('0xdd', 0x3fc)]), # New; leaving a gap to the right +Pattern('0xaa', 0x001, +3*GRANULARITY), # Overwrite and 2x Adjacent-right +Pattern('0xbb', 0x00d8000), # Partial-left (1M-160K) +Pattern('0xcc', 0x2028000), # Partial-right (32M+160K) +Pattern('0xdd', 0x3fc)]), # New; leaving a gap to the right ] class Drive: -- 2.21.0
[Qemu-devel] [PATCH v2 00/11] bitmaps: allow bitmaps to be used with full and top
Based-on: https://github.com/jnsnow/qemu/tree/bitmaps This follows the previous series which adds the 'bitmap' sync mode and uses it to add interactions with bitmaps to the 'full' and 'top' modes to blockdev-backup and drive-backup. Why? on-success: Can conveniently synchronize a bitmap to a full backup. Allows for transactionless anchor backups. Allows us to attempt an anchor backup without damaging our bitmap until the backup is successful. Allows for transactional, ungrouped anchor backups. always: Allows us to resume full/top style backups with a later invocation to sync=bitmap. Neat! Summary: 1-3: Refactor iotest 257 to accommodate this; 4-5: Augment 257 to test trivial failure cases 6-9: Refactor sync=top for block/backup 10: Implement feature 11: Test feature === V2: === Key: [] : patches are identical [] : number of functional differences between upstream/downstream patch [down] : patch is downstream-only The flags [FC] indicate (F)unctional and (C)ontextual differences, respectively 001/11:[0010] [FC] 'iotests/257: add Pattern class' 002/11:[0003] [FC] 'iotests/257: add EmulatedBitmap class' 003/11:[0042] [FC] 'iotests/257: Refactor backup helpers' 004/11:[] [--] 'block/backup: hoist bitmap check into QMP interface' 005/11:[0016] [FC] 'iotests/257: test API failures' 006/11:[down] 'block/backup: improve sync=bitmap work estimates' 007/11:[down] 'block/backup: centralize copy_bitmap initialization' 008/11:[down] 'block/backup: add backup_is_cluster_allocated' 009/11:[down] 'block/backup: teach TOP to never copy unallocated regions' 010/11:[0002] [FC] 'block/backup: support bitmap sync modes for non-bitmap backups' 011/11:[0058] [FC] 'iotests/257: test traditional sync modes' 001: Fallout from changing floor(x / y) to x // y. Fallout from changing x = x | y to x |= y. (Decided not to keep RB.) 002: Removed dead variable Change x = x - y to x -= y in clear_bits() 003: Substantially reorganize patch. Hopefully 10% more clever and 10% less cute. 004: (Added RB.) 005: Added docstring to test_backup_api Fixed test matrix enumeration to have consistent order in python2/3 (Declined RB.) ---: What was patch 006 was dropped, and became patches 6-9. 6-9: New! 010: Was 007; Formatting fix. (Added RB.) 011: Was 008; Adjust bitmap clearing/expected code as a consequence of #9. Fallout from changes to 005. John Snow (11): iotests/257: add Pattern class iotests/257: add EmulatedBitmap class iotests/257: Refactor backup helpers block/backup: hoist bitmap check into QMP interface iotests/257: test API failures block/backup: improve sync=bitmap work estimates block/backup: centralize copy_bitmap initialization block/backup: add backup_is_cluster_allocated block/backup: teach TOP to never copy unallocated regions block/backup: support bitmap sync modes for non-bitmap backups iotests/257: test traditional sync modes block/backup.c | 188 +- block/trace-events |1 + blockdev.c | 32 + qapi/block-core.json |6 +- tests/qemu-iotests/256.out |4 +- tests/qemu-iotests/257 | 342 ++-- tests/qemu-iotests/257.out | 3366 +++- 7 files changed, 3683 insertions(+), 256 deletions(-) -- 2.21.0
[Qemu-devel] [PATCH v2 02/11] iotests/257: add EmulatedBitmap class
Represent a bitmap with an object that we can mark and clear bits in. This makes it easier to manage partial writes when we don't write a full group's worth of patterns before an error. Signed-off-by: John Snow --- tests/qemu-iotests/257 | 124 + 1 file changed, 75 insertions(+), 49 deletions(-) diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index 02f9ae0649..bc66ea03b2 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -85,6 +85,59 @@ GROUPS = [ Pattern('0xdd', 0x3fc)]), # New; leaving a gap to the right ] + +class EmulatedBitmap: +def __init__(self, granularity=GRANULARITY): +self._bits = set() +self.granularity = granularity + +def dirty_bits(self, bits): +self._bits |= set(bits) + +def dirty_group(self, n): +self.dirty_bits(GROUPS[n].bits(self.granularity)) + +def clear(self): +self._bits = set() + +def clear_bits(self, bits): +self._bits -= set(bits) + +def clear_bit(self, bit): +self.clear_bits({bit}) + +def clear_group(self, n): +self.clear_bits(GROUPS[n].bits(self.granularity)) + +@property +def first_bit(self): +return sorted(self.bits)[0] + +@property +def bits(self): +return self._bits + +@property +def count(self): +return len(self.bits) + +def compare(self, qmp_bitmap): +""" +Print a nice human-readable message checking that a bitmap as reported +by the QMP interface has as many bits set as we expect it to. +""" + +name = qmp_bitmap.get('name', '(anonymous)') +log("= Checking Bitmap {:s} =".format(name)) + +want = self.count +have = qmp_bitmap['count'] // qmp_bitmap['granularity'] + +log("expecting {:d} dirty sectors; have {:d}. {:s}".format( +want, have, "OK!" if want == have else "ERROR!")) +log('') + + class Drive: """Represents, vaguely, a drive attached to a VM. Includes format, graph, and device information.""" @@ -195,27 +248,6 @@ def perform_writes(drive, n): log('') return bitmaps -def calculate_bits(groups=None): -"""Calculate how many bits we expect to see dirtied.""" -if groups: -bits = set.union(*(GROUPS[group].bits(GRANULARITY) for group in groups)) -return len(bits) -return 0 - -def bitmap_comparison(bitmap, groups=None, want=0): -""" -Print a nice human-readable message checking that this bitmap has as -many bits set as we expect it to. -""" -log("= Checking Bitmap {:s} =".format(bitmap.get('name', '(anonymous)'))) - -if groups: -want = calculate_bits(groups) -have = bitmap['count'] // bitmap['granularity'] - -log("expecting {:d} dirty sectors; have {:d}. {:s}".format( -want, have, "OK!" if want == have else "ERROR!")) -log('') def compare_images(image, reference, baseimg=None, expected_match=True): """ @@ -321,12 +353,13 @@ def test_bitmap_sync(bsync_mode, failure=None): vm.qmp_log("block-dirty-bitmap-add", node=drive0.name, name="bitmap0", granularity=GRANULARITY) log('') +ebitmap = EmulatedBitmap() # 1 - Writes and Reference Backup bitmaps = perform_writes(drive0, 1) -dirty_groups = {1} +ebitmap.dirty_group(1) bitmap = get_bitmap(bitmaps, drive0.device, 'bitmap0') -bitmap_comparison(bitmap, groups=dirty_groups) +ebitmap.compare(bitmap) reference_backup(drive0, 1, fbackup1) # 1 - Bitmap Backup (Optional induced failure) @@ -342,54 +375,47 @@ def test_bitmap_sync(bsync_mode, failure=None): log('') bitmaps = perform_writes(drive0, 2) # Named bitmap (static, should be unchanged) -bitmap_comparison(get_bitmap(bitmaps, drive0.device, 'bitmap0'), - groups=dirty_groups) +ebitmap.compare(get_bitmap(bitmaps, drive0.device, 'bitmap0')) # Anonymous bitmap (dynamic, shows new writes) -bitmap_comparison(get_bitmap(bitmaps, drive0.device, '', - recording=True), groups={2}) -dirty_groups.add(2) +anonymous = EmulatedBitmap() +anonymous.dirty_group(2) +anonymous.compare(get_bitmap(bitmaps, drive0.device, '', + recording=True)) + +# Simulate the order in which this will happen: +# group 1 gets cleared first, then group two gets written. +if ((bsync_mode == 'on-success' and not failure) or +(bsync_mode == 'always')): +ebitmap.clear_group(1) +ebitmap.dirty_group(2) vm.run_job(job, auto_dismiss=True, auto_finalize=False, pre_finalize=_callback, cancel=(failure ==
[Qemu-devel] [PATCH v2 10/11] block/backup: support bitmap sync modes for non-bitmap backups
Accept bitmaps and sync policies for the other backup modes. This allows us to do things like create a bitmap synced to a full backup without a transaction, or start a resumable backup process. Some combinations don't make sense, though: - NEVER policy combined with any non-BITMAP mode doesn't do anything, because the bitmap isn't used for input or output. It's harmless, but is almost certainly never what the user wanted. - sync=NONE is more questionable. It can't use on-success because this job never completes with success anyway, and the resulting artifact of 'always' is suspect: because we start with a full bitmap and only copy out segments that get written to, the final output bitmap will always be ... a fully set bitmap. Maybe there's contexts in which bitmaps make sense for sync=none, but not without more severe changes to the current job, and omitting it here doesn't prevent us from adding it later. Signed-off-by: John Snow --- block/backup.c | 8 +--- blockdev.c | 22 ++ qapi/block-core.json | 6 -- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/block/backup.c b/block/backup.c index e28fd23f6a..47628aca24 100644 --- a/block/backup.c +++ b/block/backup.c @@ -686,7 +686,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } -if (sync_mode == MIRROR_SYNC_MODE_BITMAP) { +if (sync_bitmap) { /* If we need to write to this bitmap, check that we can: */ if (bitmap_mode != BITMAP_SYNC_MODE_NEVER && bdrv_dirty_bitmap_check(sync_bitmap, BDRV_BITMAP_DEFAULT, errp)) { @@ -697,12 +697,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) { return NULL; } -} else if (sync_bitmap) { -error_setg(errp, - "a bitmap was given to backup_job_create, " - "but it received an incompatible sync_mode (%s)", - MirrorSyncMode_str(sync_mode)); -return NULL; } len = bdrv_getlength(bs); diff --git a/blockdev.c b/blockdev.c index 3c76c85cb5..29c6c6044a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3565,6 +3565,28 @@ static BlockJob *do_backup_common(BackupCommon *backup, if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) { return NULL; } + +/* This does not produce a useful bitmap artifact: */ +if (backup->sync == MIRROR_SYNC_MODE_NONE) { +error_setg(errp, "sync mode '%s' does not produce meaningful bitmap" + " outputs", MirrorSyncMode_str(backup->sync)); +return NULL; +} + +/* If the bitmap isn't used for input or output, this is useless: */ +if (backup->bitmap_mode == BITMAP_SYNC_MODE_NEVER && +backup->sync != MIRROR_SYNC_MODE_BITMAP) { +error_setg(errp, "Bitmap sync mode '%s' has no meaningful effect" + " when combined with sync mode '%s'", + BitmapSyncMode_str(backup->bitmap_mode), + MirrorSyncMode_str(backup->sync)); +return NULL; +} +} + +if (!backup->has_bitmap && backup->has_bitmap_mode) { +error_setg(errp, "Cannot specify bitmap sync mode without a bitmap"); +return NULL; } if (!backup->auto_finalize) { diff --git a/qapi/block-core.json b/qapi/block-core.json index 5a578806c5..099e4f37b2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1352,13 +1352,15 @@ # @speed: the maximum speed, in bytes per second. The default is 0, # for unlimited. # -# @bitmap: the name of a dirty bitmap if sync is "bitmap" or "incremental". +# @bitmap: The name of a dirty bitmap to use. # Must be present if sync is "bitmap" or "incremental". +# Can be present if sync is "full" or "top". # Must not be present otherwise. # (Since 2.4 (drive-backup), 3.1 (blockdev-backup)) # # @bitmap-mode: Specifies the type of data the bitmap should contain after -# the operation concludes. Must be present if sync is "bitmap". +# the operation concludes. +# Must be present if a bitmap was provided, # Must NOT be present otherwise. (Since 4.2) # # @compress: true to compress data, if the target format supports it. -- 2.21.0
Re: [Qemu-devel] [PATCH-for-4.1 v2 9/9] NSIS: Add missing firmware blobs
On 07/15/19 19:48, Philippe Mathieu-Daudé wrote: > Various firmwares has been added in the pc-bios/ directory: > > - CCW (since commit 0c1fecdd523) > - Skiboot (since commit bcad45de6a0) > - EDK2(since commit f7fa38b74c3) > > Since we install qemu-system able to run the architectures > targetted by these firmware, include them in the NSIS exe. > > Signed-off-by: Philippe Mathieu-Daudé > --- > qemu.nsi | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/qemu.nsi b/qemu.nsi > index 75f1608b9e..89c7c04f95 100644 > --- a/qemu.nsi > +++ b/qemu.nsi > @@ -122,6 +122,9 @@ Section "${PRODUCT} (required)" > File "${BINDIR}\*.bmp" > File "${BINDIR}\*.bin" > File "${BINDIR}\*.dtb" > +File "${BINDIR}\*.fd" > +File "${BINDIR}\*.img" > +File "${BINDIR}\*.lid" > File "${BINDIR}\*.rom" > File "${BINDIR}\openbios-*" > > This is the only patch I can (remotely) comment on... So I guess, from another patch, NSIS stands for "Nullsoft Installer". Acked-by: Laszlo Ersek I guess :) Laszlo
[Qemu-devel] Any function to access nested guest virtual address space?
Hi, All, I know this function cpu_memory_rw_debug() allows us to access a virtual machine's virtual memory address. Is there any similar function that allows us to access the virtual memory space of a nested virtual machine? i.e., L2. Thanks. -Jidong
Re: [Qemu-devel] [Qemu-riscv] [PATCH 2/2] riscv: sifive_u: Update the plic hart config to support multicore
On Sat, Jul 13, 2019 at 8:23 PM Bin Meng wrote: > > Hi Fabien, > > On Tue, Jul 9, 2019 at 12:31 AM Fabien Chouteau wrote: > > > > Hi Bin, > > > > Thanks for this patch. > > > > I know I am very late to the game but I have a comment here. > > > > On 17/05/2019 17:51, Bin Meng wrote: > > > +/* create PLIC hart topology configuration string */ > > > +plic_hart_config_len = (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1) * > > > smp_cpus; > > > +plic_hart_config = g_malloc0(plic_hart_config_len); > > > +for (i = 0; i < smp_cpus; i++) { > > > +if (i != 0) { > > > +strncat(plic_hart_config, ",", plic_hart_config_len); > > > +} > > > +strncat(plic_hart_config, SIFIVE_U_PLIC_HART_CONFIG, > > > +plic_hart_config_len); > > > +plic_hart_config_len -= (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1); > > > +} > > > + > > > > This will create up to 4 MS PLIC devices. However on the Unleashed FU540 > > the PLICs are M,MS,MS,MS,MS because of the monitor hart #0. > > > > This means a different memory layout than the real hardware. > > > > For instance address 0x0C00_2080 will be hart #0 S-Mode interrupt enables > > in QEMU, instead of #1 M-Mode interrupt enables for the real hardware. > > Thanks for the notes! I agree to better match the real hardware, it > should be modeled like that. However I am not sure what the original > intention was when creating the "sifive_u" machine. Both OpenSBI and > U-Boot list sifive_u as a special target, instead of the real > Unleashed board hence I assume this is a hypothetical target too, like > the "virt", but was created to best match the real Unleashed board > though. I thought (Palmer correct me if I'm wrong) that the sifive_u machine *should* match the hardware. The problem is that QEMU doesn't support everything that the HW supports which is why U-Boot and OpenSBI have their own targets. The goal is to not require special QEMU targets, so this is a step in the right direction. Alistair > > > > > To fix this I suggest to change this loop to: > > > > for (i = 0; i < smp_cpus; i++) { > > if (i != 0) { > > strncat(plic_hart_config, "," SIFIVE_U_PLIC_HART_CONFIG, > > plic_hart_config_len); > > } else { > > strncat(plic_hart_config, "M", plic_hart_config_len); > > } > > plic_hart_config_len -= (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1); > > } > > > > This will make hart #0 PLIC in M mode and the others in MS. > > > > What do you think? > > > Regards, > Bin >
Re: [Qemu-devel] [PATCH v2 00/14] Multiple simultaneous audio backends
Patchew URL: https://patchew.org/QEMU/cover.1563224628.git.dirty.ice...@gmail.com/ Hi, This series seems to have some coding style problems. See output below for more information: Message-id: cover.1563224628.git.dirty.ice...@gmail.com Type: series Subject: [Qemu-devel] [PATCH v2 00/14] Multiple simultaneous audio backends === TEST SCRIPT BEGIN === #!/bin/bash git rev-parse base > /dev/null || exit 0 git config --local diff.renamelimit 0 git config --local diff.renames True git config --local diff.algorithm histogram ./scripts/checkpatch.pl --mailback base.. === TEST SCRIPT END === Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384 Switched to a new branch 'test' 9bd8bc6 audio: fix memory leak reported by ASAN e8d23f6 audio: use size_t where makes sense 7035d9f audio: remove read and write pcm_ops 82038f1 paaudio: fix playback glitches ad773f9 audio: do not run each backend in audio_run e5bae54 audio: remove audio_MIN, audio_MAX 3f525e4 paaudio: properly disconnect streams in fini_* e76429c paaudio: do not move stream when sink/source name is specified 232f925 audio: audiodev= parameters no longer optional when -audiodev present 0475ef4 paaudio: prepare for multiple audiodev d0ccdb3 audio: add audiodev properties to frontends b8bbf6b audio: add audiodev property to vnc and wav_capture 45bece9 audio: basic support for multi backend audio b8ed8fa audio: reduce glob_audio_state usage === OUTPUT BEGIN === 1/14 Checking commit b8ed8fa72e64 (audio: reduce glob_audio_state usage) 2/14 Checking commit 45bece9445ff (audio: basic support for multi backend audio) 3/14 Checking commit b8bbf6b1534a (audio: add audiodev property to vnc and wav_capture) 4/14 Checking commit d0ccdb31831d (audio: add audiodev properties to frontends) 5/14 Checking commit 0475ef4aa556 (paaudio: prepare for multiple audiodev) 6/14 Checking commit 232f925703f4 (audio: audiodev= parameters no longer optional when -audiodev present) 7/14 Checking commit e76429c12dda (paaudio: do not move stream when sink/source name is specified) 8/14 Checking commit 3f525e459011 (paaudio: properly disconnect streams in fini_*) 9/14 Checking commit e5bae54fc748 (audio: remove audio_MIN, audio_MAX) ERROR: space prohibited between function name and open parenthesis '(' #23: FILE: audio/alsaaudio.c:637: +int len = MIN (alsa->pending, left_till_end_samples); ERROR: space prohibited between function name and open parenthesis '(' #32: FILE: audio/alsaaudio.c:700: +decr = MIN (live, avail); ERROR: space prohibited between function name and open parenthesis '(' #41: FILE: audio/alsaaudio.c:918: +decr = MIN (dead, avail); ERROR: space prohibited between function name and open parenthesis '(' #54: FILE: audio/audio.c:536: +m = MIN (m, sw->total_hw_samples_acquired); ERROR: space prohibited between function name and open parenthesis '(' #63: FILE: audio/audio.c:556: +int len = MIN (left, live); ERROR: space prohibited between function name and open parenthesis '(' #71: FILE: audio/audio.c:563: +int samples_to_clip = MIN (len, samples_till_end_of_buf); ERROR: space prohibited between function name and open parenthesis '(' #80: FILE: audio/audio.c:617: +swlim = MIN (swlim, samples); ERROR: space prohibited between function name and open parenthesis '(' #89: FILE: audio/audio.c:665: +m = MIN (m, sw->total_hw_samples_mixed); ERROR: space prohibited between function name and open parenthesis '(' #98: FILE: audio/audio.c:728: +swlim = MIN (swlim, samples); ERROR: space prohibited between function name and open parenthesis '(' #107: FILE: audio/audio.c:740: +blck = MIN (dead, left); ERROR: space prohibited between function name and open parenthesis '(' #116: FILE: audio/audio.c:1032: +int to_write = MIN (till_end_of_hw, n); ERROR: space prohibited between function name and open parenthesis '(' #125: FILE: audio/audio.c:1050: +n = MIN (samples, hw->samples - rpos); ERROR: space prohibited between function name and open parenthesis '(' #134: FILE: audio/audio.c:1206: +int to_capture = MIN (live, left); ERROR: space prohibited between function name and open parenthesis '(' #175: FILE: audio/coreaudio.c:416: +decr = MIN (core->decr, live); ERROR: space prohibited between function name and open parenthesis '(' #188: FILE: audio/dsoundaudio.c:710: +len = MIN (len, dead); ERROR: space prohibited between function name and open parenthesis '(' #206: FILE: audio/noaudio.c:59: +decr = MIN (live, samples); ERROR: space prohibited between function name and open parenthesis '(' #215: FILE: audio/noaudio.c:114: +bytes = MIN (bytes, INT_MAX); ERROR: space prohibited between function name and open parenthesis '(' #218: FILE: audio/noaudio.c:116: +samples = MIN (samples, dead); ERROR: space prohibited between function name and open parenthesis '(' #227: FILE: audio/noaudio.c:127: +int to_clear = MIN (samples, total); ERROR: space
[Qemu-devel] [PATCH v2 13/14] audio: use size_t where makes sense
Signed-off-by: Kővágó, Zoltán --- audio/audio.h | 4 +- audio/audio_int.h | 26 +++ audio/audio_template.h | 14 ++-- audio/mixeng.h | 9 +-- audio/rate_template.h | 2 +- include/sysemu/replay.h | 4 +- audio/alsaaudio.c | 26 +++ audio/audio.c | 156 audio/coreaudio.c | 10 +-- audio/dsoundaudio.c | 11 +-- audio/noaudio.c | 16 ++--- audio/ossaudio.c| 45 ++-- audio/paaudio.c | 44 ++-- audio/sdlaudio.c| 20 +++--- audio/spiceaudio.c | 12 ++-- audio/wavaudio.c| 8 +-- replay/replay-audio.c | 16 ++--- replay/replay.c | 2 +- 18 files changed, 212 insertions(+), 213 deletions(-) diff --git a/audio/audio.h b/audio/audio.h index 4a95758516..2db27bba7b 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -112,7 +112,7 @@ SWVoiceOut *AUD_open_out ( ); void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); -int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size); +size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size); int AUD_get_buffer_size_out (SWVoiceOut *sw); void AUD_set_active_out (SWVoiceOut *sw, int on); int AUD_is_active_out (SWVoiceOut *sw); @@ -133,7 +133,7 @@ SWVoiceIn *AUD_open_in ( ); void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); -int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size); +size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size); void AUD_set_active_in (SWVoiceIn *sw, int on); int AUD_is_active_in (SWVoiceIn *sw); diff --git a/audio/audio_int.h b/audio/audio_int.h index 003b7ab8cc..a674c5374a 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -61,12 +61,12 @@ typedef struct HWVoiceOut { f_sample *clip; -int rpos; +size_t rpos; uint64_t ts_helper; struct st_sample *mix_buf; -int samples; +size_t samples; QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; int ctl_caps; @@ -82,13 +82,13 @@ typedef struct HWVoiceIn { t_sample *conv; -int wpos; -int total_samples_captured; +size_t wpos; +size_t total_samples_captured; uint64_t ts_helper; struct st_sample *conv_buf; -int samples; +size_t samples; QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; int ctl_caps; struct audio_pcm_ops *pcm_ops; @@ -103,7 +103,7 @@ struct SWVoiceOut { int64_t ratio; struct st_sample *buf; void *rate; -int total_hw_samples_mixed; +size_t total_hw_samples_mixed; int active; int empty; HWVoiceOut *hw; @@ -120,7 +120,7 @@ struct SWVoiceIn { struct audio_pcm_info info; int64_t ratio; void *rate; -int total_hw_samples_acquired; +size_t total_hw_samples_acquired; struct st_sample *buf; f_sample *clip; HWVoiceIn *hw; @@ -149,12 +149,12 @@ struct audio_driver { struct audio_pcm_ops { int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); void (*fini_out)(HWVoiceOut *hw); -int (*run_out) (HWVoiceOut *hw, int live); +size_t (*run_out)(HWVoiceOut *hw, size_t live); int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque); void (*fini_in) (HWVoiceIn *hw); -int (*run_in) (HWVoiceIn *hw); +size_t (*run_in)(HWVoiceIn *hw); int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); }; @@ -208,10 +208,10 @@ audio_driver *audio_driver_lookup(const char *name); void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); -int audio_pcm_hw_get_live_in (HWVoiceIn *hw); +size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw); -int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, - int live, int pending); +size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, + size_t live, size_t pending); int audio_bug (const char *funcname, int cond); void *audio_calloc (const char *funcname, int nmemb, size_t size); @@ -224,7 +224,7 @@ void audio_run(AudioState *s, const char *msg); #define VOICE_VOLUME_CAP (1 << VOICE_VOLUME) -static inline int audio_ring_dist (int dst, int src, int len) +static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len) { return (dst >= src) ? (dst - src) : (len - src + dst); } diff --git a/audio/audio_template.h b/audio/audio_template.h index 54f07338e7..2562bf5f00 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -75,16 +75,16 @@ static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) HWBUF = NULL; } -static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) +static bool glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) { HWBUF = audio_calloc(__func__, hw->samples,
Re: [Qemu-devel] [RFC PATCH-for-4.1? v2 0/5] semihosting: Build with CONFIG_SEMIHOSTING disabled
Patchew URL: https://patchew.org/QEMU/20190715152225.26135-1-phi...@redhat.com/ Hi, This series failed build test on s390x host. Please find the details below. === TEST SCRIPT BEGIN === #!/bin/bash # Testing script will be invoked under the git checkout with # HEAD pointing to a commit that has the patches applied on top of "base" # branch set -e echo echo "=== ENV ===" env echo echo "=== PACKAGES ===" rpm -qa echo echo "=== UNAME ===" uname -a CC=$HOME/bin/cc INSTALL=$PWD/install BUILD=$PWD/build mkdir -p $BUILD $INSTALL SRC=$PWD cd $BUILD $SRC/configure --cc=$CC --prefix=$INSTALL make -j4 # XXX: we need reliable clean up # make check -j4 V=1 make install === TEST SCRIPT END === CC mips64-linux-user/linux-user/fd-trans.o CC mips64-linux-user/target/mips/translate.o CC mips64el-linux-user/target/mips/mips-semi-stubs.o /var/tmp/patchew-tester-tmp-n0mrs_zw/src/target/mips/mips-semi-stubs.c:19:6: error: no previous prototype for ‘helper_do_semihosting’ [-Werror=missing-prototypes] 19 | void helper_do_semihosting(CPUMIPSState *env) | ^ cc1: all warnings being treated as errors --- CC mipsel-linux-user/target/mips/translate.o CC mips64-linux-user/target/mips/msa_helper.o CC mips64-linux-user/target/mips/mips-semi-stubs.o /var/tmp/patchew-tester-tmp-n0mrs_zw/src/target/mips/mips-semi-stubs.c:19:6: error: no previous prototype for ‘helper_do_semihosting’ [-Werror=missing-prototypes] 19 | void helper_do_semihosting(CPUMIPSState *env) | ^ cc1: all warnings being treated as errors --- CC mipsel-linux-user/target/mips/mips-semi-stubs.o GEN trace/generated-helpers.c CC mipsel-linux-user/trace/control-target.o /var/tmp/patchew-tester-tmp-n0mrs_zw/src/target/mips/mips-semi-stubs.c:19:6: error: no previous prototype for ‘helper_do_semihosting’ [-Werror=missing-prototypes] 19 | void helper_do_semihosting(CPUMIPSState *env) | ^ cc1: all warnings being treated as errors The full log is available at http://patchew.org/logs/20190715152225.26135-1-phi...@redhat.com/testing.s390x/?type=message. --- Email generated automatically by Patchew [https://patchew.org/]. Please send your feedback to patchew-de...@redhat.com
[Qemu-devel] [PATCH v2 09/14] audio: remove audio_MIN, audio_MAX
There's already a MIN and MAX macro in include/qemu/osdep.h, use them instead. Signed-off-by: Kővágó, Zoltán Reviewed-by: Marc-André Lureau --- audio/audio.h | 17 - audio/alsaaudio.c | 6 +++--- audio/audio.c | 20 ++-- audio/coreaudio.c | 2 +- audio/dsoundaudio.c | 2 +- audio/noaudio.c | 10 +- audio/ossaudio.c | 6 +++--- audio/paaudio.c | 12 ++-- audio/sdlaudio.c | 6 +++--- audio/spiceaudio.c| 10 +- audio/wavaudio.c | 4 ++-- hw/audio/ac97.c | 10 +- hw/audio/adlib.c | 4 ++-- hw/audio/cs4231a.c| 4 ++-- hw/audio/es1370.c | 6 +++--- hw/audio/gus.c| 6 +++--- hw/audio/hda-codec.c | 16 hw/audio/milkymist-ac97.c | 8 hw/audio/pcspk.c | 2 +- hw/audio/sb16.c | 2 +- hw/audio/wm8750.c | 4 ++-- 21 files changed, 70 insertions(+), 87 deletions(-) diff --git a/audio/audio.h b/audio/audio.h index c0722a5cda..4a95758516 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -146,23 +146,6 @@ static inline void *advance (void *p, int incr) return (d + incr); } -#ifdef __GNUC__ -#define audio_MIN(a, b) ( __extension__ ({ \ -__typeof (a) ta = a;\ -__typeof (b) tb = b;\ -((ta)>(tb)?(tb):(ta)); \ -})) - -#define audio_MAX(a, b) ( __extension__ ({ \ -__typeof (a) ta = a;\ -__typeof (b) tb = b;\ -((ta)<(tb)?(tb):(ta)); \ -})) -#else -#define audio_MIN(a, b) ((a)>(b)?(b):(a)) -#define audio_MAX(a, b) ((a)<(b)?(b):(a)) -#endif - int wav_start_capture(AudioState *state, CaptureState *s, const char *path, int freq, int bits, int nchannels); diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 3745c823ad..6b9e0f06af 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -634,7 +634,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa) while (alsa->pending) { int left_till_end_samples = hw->samples - alsa->wpos; -int len = audio_MIN (alsa->pending, left_till_end_samples); +int len = MIN (alsa->pending, left_till_end_samples); char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift); while (len) { @@ -697,7 +697,7 @@ static int alsa_run_out (HWVoiceOut *hw, int live) return 0; } -decr = audio_MIN (live, avail); +decr = MIN (live, avail); decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending); alsa->pending += decr; alsa_write_pending (alsa); @@ -915,7 +915,7 @@ static int alsa_run_in (HWVoiceIn *hw) } } -decr = audio_MIN (dead, avail); +decr = MIN (dead, avail); if (!decr) { return 0; } diff --git a/audio/audio.c b/audio/audio.c index d131958194..cb0222ab4a 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -533,7 +533,7 @@ static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (sw->active) { -m = audio_MIN (m, sw->total_hw_samples_acquired); +m = MIN (m, sw->total_hw_samples_acquired); } } return m; @@ -553,14 +553,14 @@ int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, int live, int pending) { int left = hw->samples - pending; -int len = audio_MIN (left, live); +int len = MIN (left, live); int clipped = 0; while (len) { struct st_sample *src = hw->mix_buf + hw->rpos; uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift); int samples_till_end_of_buf = hw->samples - hw->rpos; -int samples_to_clip = audio_MIN (len, samples_till_end_of_buf); +int samples_to_clip = MIN (len, samples_till_end_of_buf); hw->clip (dst, src, samples_to_clip); @@ -614,7 +614,7 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) } swlim = (live * sw->ratio) >> 32; -swlim = audio_MIN (swlim, samples); +swlim = MIN (swlim, samples); while (swlim) { src = hw->conv_buf + rpos; @@ -662,7 +662,7 @@ static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (sw->active || !sw->empty) { -m = audio_MIN (m, sw->total_hw_samples_mixed); +m = MIN (m, sw->total_hw_samples_mixed); nb_live += 1; } } @@ -725,7 +725,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) dead = hwsamples - live; swlim = ((int64_t) dead << 32) / sw->ratio; -swlim = audio_MIN (swlim, samples); +swlim = MIN (swlim, samples); if (swlim) { sw->conv (sw->buf, buf,
[Qemu-devel] [PATCH v2 08/14] paaudio: properly disconnect streams in fini_*
Currently this needs a workaround due to bug #247 in pulseaudio. Signed-off-by: Kővágó, Zoltán Reviewed-by: Marc-André Lureau --- audio/paaudio.c | 25 +++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/audio/paaudio.c b/audio/paaudio.c index 24d98b344a..1d68173636 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -685,6 +685,27 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) return -1; } +static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream) +{ +int err; + +pa_threaded_mainloop_lock(c->mainloop); +/* + * wait until actually connects. workaround pa bug #247 + * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247 + */ +while (pa_stream_get_state(stream) == PA_STREAM_CREATING) { +pa_threaded_mainloop_wait(c->mainloop); +} + +err = pa_stream_disconnect(stream); +if (err != 0) { +dolog("Failed to disconnect! err=%d\n", err); +} +pa_stream_unref(stream); +pa_threaded_mainloop_unlock(c->mainloop); +} + static void qpa_fini_out (HWVoiceOut *hw) { void *ret; @@ -696,7 +717,7 @@ static void qpa_fini_out (HWVoiceOut *hw) audio_pt_join(>pt, , __func__); if (pa->stream) { -pa_stream_unref (pa->stream); +qpa_simple_disconnect(pa->g->conn, pa->stream); pa->stream = NULL; } @@ -716,7 +737,7 @@ static void qpa_fini_in (HWVoiceIn *hw) audio_pt_join(>pt, , __func__); if (pa->stream) { -pa_stream_unref (pa->stream); +qpa_simple_disconnect(pa->g->conn, pa->stream); pa->stream = NULL; } -- 2.22.0
[Qemu-devel] [PATCH v2 10/14] audio: do not run each backend in audio_run
audio_run is called manually by alsa and oss backends when polling. In this case only the requesting backend should be run, not all of them. Signed-off-by: Kővágó, Zoltán Reviewed-by: Marc-André Lureau --- audio/audio_int.h | 2 +- audio/alsaaudio.c | 7 +-- audio/audio.c | 14 +- audio/ossaudio.c | 12 ++-- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/audio/audio_int.h b/audio/audio_int.h index 9f01f6ad00..7e00c1332e 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -221,7 +221,7 @@ int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, int audio_bug (const char *funcname, int cond); void *audio_calloc (const char *funcname, int nmemb, size_t size); -void audio_run (const char *msg); +void audio_run(AudioState *s, const char *msg); #define VOICE_ENABLE 1 #define VOICE_DISABLE 2 diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 6b9e0f06af..3daa7c8f8f 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -39,6 +39,7 @@ struct pollhlp { struct pollfd *pfds; int count; int mask; +AudioState *s; }; typedef struct ALSAVoiceOut { @@ -199,11 +200,11 @@ static void alsa_poll_handler (void *opaque) break; case SND_PCM_STATE_PREPARED: -audio_run ("alsa run (prepared)"); +audio_run(hlp->s, "alsa run (prepared)"); break; case SND_PCM_STATE_RUNNING: -audio_run ("alsa run (running)"); +audio_run(hlp->s, "alsa run (running)"); break; default: @@ -749,6 +750,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, return -1; } +alsa->pollhlp.s = hw->s; alsa->handle = handle; alsa->dev = dev; return 0; @@ -850,6 +852,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) return -1; } +alsa->pollhlp.s = hw->s; alsa->handle = handle; alsa->dev = dev; return 0; diff --git a/audio/audio.c b/audio/audio.c index cb0222ab4a..07b35ffa03 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -835,7 +835,7 @@ static void audio_timer (void *opaque) } s->timer_last = now; -audio_run("timer"); +audio_run(s, "timer"); audio_reset_timer(s); } @@ -1237,15 +1237,11 @@ static void audio_run_capture (AudioState *s) } } -void audio_run (const char *msg) +void audio_run(AudioState *s, const char *msg) { -AudioState *s; - -QTAILQ_FOREACH(s, _states, list) { -audio_run_out(s); -audio_run_in(s); -audio_run_capture(s); -} +audio_run_out(s); +audio_run_in(s); +audio_run_capture(s); #ifdef DEBUG_POLL { diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 29139ef1f5..456fba7480 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -110,28 +110,28 @@ static void oss_anal_close (int *fdp) static void oss_helper_poll_out (void *opaque) { -(void) opaque; -audio_run ("oss_poll_out"); +AudioState *s = opaque; +audio_run(s, "oss_poll_out"); } static void oss_helper_poll_in (void *opaque) { -(void) opaque; -audio_run ("oss_poll_in"); +AudioState *s = opaque; +audio_run(s, "oss_poll_in"); } static void oss_poll_out (HWVoiceOut *hw) { OSSVoiceOut *oss = (OSSVoiceOut *) hw; -qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); +qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s); } static void oss_poll_in (HWVoiceIn *hw) { OSSVoiceIn *oss = (OSSVoiceIn *) hw; -qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); +qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s); } static int oss_write (SWVoiceOut *sw, void *buf, int len) -- 2.22.0
[Qemu-devel] [PATCH v2 07/14] paaudio: do not move stream when sink/source name is specified
Unless we disable stream moving, pulseaudio can easily move the stream on connect, effectively ignoring the source/sink specified by the user. Signed-off-by: Kővágó, Zoltán Reviewed-by: Marc-André Lureau --- audio/paaudio.c | 5 + 1 file changed, 5 insertions(+) diff --git a/audio/paaudio.c b/audio/paaudio.c index cc3a34c2ea..24d98b344a 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -517,6 +517,11 @@ static pa_stream *qpa_simple_new ( #endif | PA_STREAM_AUTO_TIMING_UPDATE; +if (dev) { +/* don't move the stream if the user specified a sink/source */ +flags |= PA_STREAM_DONT_MOVE; +} + if (dir == PA_STREAM_PLAYBACK) { r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL); } else { -- 2.22.0
[Qemu-devel] [PATCH v2 14/14] audio: fix memory leak reported by ASAN
Signed-off-by: Kővágó, Zoltán --- audio/audio.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/audio/audio.c b/audio/audio.c index d3c639211d..ebe49f3ca1 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1343,6 +1343,12 @@ static void free_audio_state(AudioState *s) qapi_free_Audiodev(s->dev); s->dev = NULL; } + +if (s->ts) { +timer_free(s->ts); +s->ts = NULL; +} + g_free(s); } -- 2.22.0
[Qemu-devel] [PATCH v2 06/14] audio: audiodev= parameters no longer optional when -audiodev present
This means you should probably stop using -soundhw (as it doesn't allow you to specify any options) and add the device manually with -device. The exception is pcspk, it's currently not possible to manually add it. To use it with audiodev, use something like this: -audiodev id=foo,... -global isa-pcspk.audiodev=foo -soundhw pcspk Signed-off-by: Kővágó, Zoltán --- Notes: Changes from v1: * Split off paaudio changes to a different commit. audio/audio.c | 24 +++- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 4baa37caac..d131958194 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -101,6 +101,8 @@ const struct mixeng_volume nominal_volume = { #endif }; +static bool legacy_config = true; + #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED #error No its not #else @@ -1392,7 +1394,7 @@ static AudiodevListEntry *audiodev_find( * if dev == NULL => legacy implicit initialization, return the already created * state or create a new one */ -static AudioState *audio_init(Audiodev *dev) +static AudioState *audio_init(Audiodev *dev, const char *name) { static bool atexit_registered; size_t i; @@ -1406,12 +1408,13 @@ static AudioState *audio_init(Audiodev *dev) if (dev) { /* -audiodev option */ +legacy_config = false; drvname = AudiodevDriver_str(dev->driver); } else if (!QTAILQ_EMPTY(_states)) { -/* - * todo: check for -audiodev once we have normal audiodev selection - * support - */ +if (!legacy_config) { +dolog("You must specify an audiodev= for the device %s\n", name); +exit(1); +} return QTAILQ_FIRST(_states); } else { /* legacy implicit initialization */ @@ -1518,7 +1521,7 @@ void audio_free_audiodev_list(AudiodevListHead *head) void AUD_register_card (const char *name, QEMUSoundCard *card) { if (!card->state) { -card->state = audio_init(NULL); +card->state = audio_init(NULL, name); } card->name = g_strdup (name); @@ -1544,8 +1547,11 @@ CaptureVoiceOut *AUD_add_capture( struct capture_callback *cb; if (!s) { -/* todo: remove when we have normal audiodev selection support */ -s = audio_init(NULL); +if (!legacy_config) { +dolog("You must specify audiodev when trying to capture\n"); +return NULL; +} +s = audio_init(NULL, NULL); } if (audio_validate_settings (as)) { @@ -1776,7 +1782,7 @@ void audio_init_audiodevs(void) AudiodevListEntry *e; QSIMPLEQ_FOREACH(e, , next) { -audio_init(e->dev); +audio_init(e->dev, NULL); } } -- 2.22.0
[Qemu-devel] [PATCH v2 11/14] paaudio: fix playback glitches
Pulseaudio normally assumes that when the server wants it, the client can generate the audio samples and send it right away. Unfortunately this is not the case with QEMU -- it's up to the emulated system when does it generate the samples. Buffering the samples and sending them from a background thread is just a workaround, that doesn't work too well. Instead enable pa's compatibility support and let pa worry about the details. Signed-off-by: Kővágó, Zoltán --- audio/paaudio.c | 6 ++ 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/audio/paaudio.c b/audio/paaudio.c index f3864e1d50..c8ae1a6eca 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -512,10 +512,8 @@ static pa_stream *qpa_simple_new ( flags = PA_STREAM_INTERPOLATE_TIMING -#ifdef PA_STREAM_ADJUST_LATENCY -| PA_STREAM_ADJUST_LATENCY -#endif -| PA_STREAM_AUTO_TIMING_UPDATE; +| PA_STREAM_AUTO_TIMING_UPDATE +| PA_STREAM_EARLY_REQUESTS; if (dev) { /* don't move the stream if the user specified a sink/source */ -- 2.22.0
[Qemu-devel] [PATCH v2 12/14] audio: remove read and write pcm_ops
They just called audio_pcm_sw_read/write anyway, so it makes no sense to have them too. (The noaudio's read is the only exception, but it should work with the generic code too.) Signed-off-by: Kővágó, Zoltán --- audio/audio_int.h | 5 - audio/alsaaudio.c | 12 audio/audio.c | 8 audio/coreaudio.c | 6 -- audio/dsoundaudio.c | 12 audio/noaudio.c | 19 --- audio/ossaudio.c| 12 audio/paaudio.c | 12 audio/sdlaudio.c| 6 -- audio/spiceaudio.c | 12 audio/wavaudio.c| 6 -- 11 files changed, 4 insertions(+), 106 deletions(-) diff --git a/audio/audio_int.h b/audio/audio_int.h index 7e00c1332e..003b7ab8cc 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -150,13 +150,11 @@ struct audio_pcm_ops { int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); void (*fini_out)(HWVoiceOut *hw); int (*run_out) (HWVoiceOut *hw, int live); -int (*write) (SWVoiceOut *sw, void *buf, int size); int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque); void (*fini_in) (HWVoiceIn *hw); int (*run_in) (HWVoiceIn *hw); -int (*read)(SWVoiceIn *sw, void *buf, int size); int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); }; @@ -210,11 +208,8 @@ audio_driver *audio_driver_lookup(const char *name); void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); -int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); int audio_pcm_hw_get_live_in (HWVoiceIn *hw); -int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); - int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, int live, int pending); diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 3daa7c8f8f..e9e3a4819c 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -270,11 +270,6 @@ static int alsa_poll_in (HWVoiceIn *hw) return alsa_poll_helper (alsa->handle, >pollhlp, POLLIN); } -static int alsa_write (SWVoiceOut *sw, void *buf, int len) -{ -return audio_pcm_sw_write (sw, buf, len); -} - static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) { switch (fmt) { @@ -988,11 +983,6 @@ static int alsa_run_in (HWVoiceIn *hw) return read_samples; } -static int alsa_read (SWVoiceIn *sw, void *buf, int size) -{ -return audio_pcm_sw_read (sw, buf, size); -} - static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) { ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; @@ -1076,13 +1066,11 @@ static struct audio_pcm_ops alsa_pcm_ops = { .init_out = alsa_init_out, .fini_out = alsa_fini_out, .run_out = alsa_run_out, -.write= alsa_write, .ctl_out = alsa_ctl_out, .init_in = alsa_init_in, .fini_in = alsa_fini_in, .run_in = alsa_run_in, -.read = alsa_read, .ctl_in = alsa_ctl_in, }; diff --git a/audio/audio.c b/audio/audio.c index 07b35ffa03..254499dfa1 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -594,7 +594,7 @@ static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) } } -int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) +static int audio_pcm_sw_read(SWVoiceIn *sw, void *buf, int size) { HWVoiceIn *hw = sw->hw; int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; @@ -696,7 +696,7 @@ static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live) /* * Soft voice (playback) */ -int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) +static int audio_pcm_sw_write(SWVoiceOut *sw, void *buf, int size) { int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; int ret = 0, pos = 0, total = 0; @@ -854,7 +854,7 @@ int AUD_write (SWVoiceOut *sw, void *buf, int size) return 0; } -return sw->hw->pcm_ops->write(sw, buf, size); +return audio_pcm_sw_write(sw, buf, size); } int AUD_read (SWVoiceIn *sw, void *buf, int size) @@ -869,7 +869,7 @@ int AUD_read (SWVoiceIn *sw, void *buf, int size) return 0; } -return sw->hw->pcm_ops->read(sw, buf, size); +return audio_pcm_sw_read(sw, buf, size); } int AUD_get_buffer_size_out (SWVoiceOut *sw) diff --git a/audio/coreaudio.c b/audio/coreaudio.c index f0ab4014a8..091fe84a34 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -489,11 +489,6 @@ static OSStatus audioDeviceIOProc( return 0; } -static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) -{ -return audio_pcm_sw_write (sw, buf, len); -} - static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { @@ -692,7 +687,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = { .init_out = coreaudio_init_out, .fini_out =
[Qemu-devel] [PATCH v2 05/14] paaudio: prepare for multiple audiodev
Have a pool of refcounted connections per server, so if the user creates multiple audiodevs to the same pa server, it will use a single connection. (It will still create different streams, so the user can manage those streams separately in pulseaudio.) Signed-off-by: Kővágó, Zoltán --- audio/paaudio.c | 329 +++- 1 file changed, 188 insertions(+), 141 deletions(-) diff --git a/audio/paaudio.c b/audio/paaudio.c index 5fc886bb33..cc3a34c2ea 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -11,10 +11,21 @@ #include "audio_int.h" #include "audio_pt_int.h" -typedef struct { -Audiodev *dev; +typedef struct PAConnection { +char *server; +int refcount; +QTAILQ_ENTRY(PAConnection) list; + pa_threaded_mainloop *mainloop; pa_context *context; +} PAConnection; + +static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns = +QTAILQ_HEAD_INITIALIZER(pa_conns); + +typedef struct { +Audiodev *dev; +PAConnection *conn; } paaudio; typedef struct { @@ -45,7 +56,7 @@ typedef struct { int samples; } PAVoiceIn; -static void qpa_audio_fini(void *opaque); +static void qpa_conn_fini(PAConnection *c); static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) { @@ -108,11 +119,11 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) { -paaudio *g = p->g; +PAConnection *c = p->g->conn; -pa_threaded_mainloop_lock (g->mainloop); +pa_threaded_mainloop_lock(c->mainloop); -CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); +CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail); while (length > 0) { size_t l; @@ -121,11 +132,11 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror int r; r = pa_stream_peek (p->stream, >read_data, >read_length); -CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); +CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail); if (!p->read_data) { -pa_threaded_mainloop_wait (g->mainloop); -CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); +pa_threaded_mainloop_wait(c->mainloop); +CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail); } else { p->read_index = 0; } @@ -148,53 +159,53 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror p->read_length = 0; p->read_index = 0; -CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); +CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail); } } -pa_threaded_mainloop_unlock (g->mainloop); +pa_threaded_mainloop_unlock(c->mainloop); return 0; unlock_and_fail: -pa_threaded_mainloop_unlock (g->mainloop); +pa_threaded_mainloop_unlock(c->mainloop); return -1; } static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) { -paaudio *g = p->g; +PAConnection *c = p->g->conn; -pa_threaded_mainloop_lock (g->mainloop); +pa_threaded_mainloop_lock(c->mainloop); -CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); +CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail); while (length > 0) { size_t l; int r; while (!(l = pa_stream_writable_size (p->stream))) { -pa_threaded_mainloop_wait (g->mainloop); -CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); +pa_threaded_mainloop_wait(c->mainloop); +CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail); } -CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); +CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail); if (l > length) { l = length; } r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); -CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); +CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail); data = (const uint8_t *) data + l; length -= l; } -pa_threaded_mainloop_unlock (g->mainloop); +pa_threaded_mainloop_unlock(c->mainloop); return 0; unlock_and_fail: -pa_threaded_mainloop_unlock (g->mainloop); +pa_threaded_mainloop_unlock(c->mainloop); return -1; } @@ -432,13 +443,13 @@ static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness) static void context_state_cb (pa_context *c, void *userdata) { -paaudio *g = userdata; +PAConnection *conn = userdata; switch (pa_context_get_state(c)) { case PA_CONTEXT_READY: case PA_CONTEXT_TERMINATED: case PA_CONTEXT_FAILED: -pa_threaded_mainloop_signal (g->mainloop, 0); +
[Qemu-devel] [PATCH v2 02/14] audio: basic support for multi backend audio
Audio functions no longer access glob_audio_state, instead they get an AudioState as a parameter. This is required in order to support multiple backends. glob_audio_state is also gone, and replaced with a tailq so we can store more than one states. Signed-off-by: Kővágó, Zoltán --- Notes: Changes from v1: * Moved wav_capture/vnc audiodev param to a separate commit audio/audio.h | 12 +++-- audio/audio_int.h | 2 + audio/audio_template.h | 2 +- audio/audio.c | 102 +++-- audio/wavcapture.c | 6 +-- monitor/misc.c | 2 +- ui/vnc.c | 2 +- 7 files changed, 95 insertions(+), 33 deletions(-) diff --git a/audio/audio.h b/audio/audio.h index 64b0f761bc..ad2457f4de 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -78,8 +78,10 @@ typedef struct SWVoiceOut SWVoiceOut; typedef struct CaptureVoiceOut CaptureVoiceOut; typedef struct SWVoiceIn SWVoiceIn; +typedef struct AudioState AudioState; typedef struct QEMUSoundCard { char *name; +AudioState *state; QLIST_ENTRY (QEMUSoundCard) entries; } QEMUSoundCard; @@ -92,7 +94,8 @@ void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void AUD_register_card (const char *name, QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card); -CaptureVoiceOut *AUD_add_capture ( +CaptureVoiceOut *AUD_add_capture( +AudioState *s, struct audsettings *as, struct audio_capture_ops *ops, void *opaque @@ -160,8 +163,8 @@ static inline void *advance (void *p, int incr) #define audio_MAX(a, b) ((a)<(b)?(b):(a)) #endif -int wav_start_capture (CaptureState *s, const char *path, int freq, - int bits, int nchannels); +int wav_start_capture(AudioState *state, CaptureState *s, const char *path, + int freq, int bits, int nchannels); bool audio_is_cleaning_up(void); void audio_cleanup(void); @@ -175,4 +178,7 @@ void audio_parse_option(const char *opt); void audio_init_audiodevs(void); void audio_legacy_help(void); +AudioState *audio_state_by_name(const char *name); +const char *audio_get_id(QEMUSoundCard *card); + #endif /* QEMU_AUDIO_H */ diff --git a/audio/audio_int.h b/audio/audio_int.h index 8164696b2c..9f01f6ad00 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -196,6 +196,8 @@ typedef struct AudioState { bool timer_running; uint64_t timer_last; + +QTAILQ_ENTRY(AudioState) list; } AudioState; extern const struct mixeng_volume nominal_volume; diff --git a/audio/audio_template.h b/audio/audio_template.h index c721fed75d..54f07338e7 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -428,7 +428,7 @@ SW *glue (AUD_open_, TYPE) ( struct audsettings *as ) { -AudioState *s = _audio_state; +AudioState *s = card->state; AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { diff --git a/audio/audio.c b/audio/audio.c index 8d2f580788..4baa37caac 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -87,7 +87,8 @@ audio_driver *audio_driver_lookup(const char *name) return NULL; } -static AudioState glob_audio_state; +static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states = +QTAILQ_HEAD_INITIALIZER(audio_states); const struct mixeng_volume nominal_volume = { .mute = 0, @@ -1236,11 +1237,14 @@ static void audio_run_capture (AudioState *s) void audio_run (const char *msg) { -AudioState *s = _audio_state; +AudioState *s; + +QTAILQ_FOREACH(s, _states, list) { +audio_run_out(s); +audio_run_in(s); +audio_run_capture(s); +} -audio_run_out (s); -audio_run_in (s); -audio_run_capture (s); #ifdef DEBUG_POLL { static double prevtime; @@ -1304,13 +1308,11 @@ bool audio_is_cleaning_up(void) return is_cleaning_up; } -void audio_cleanup(void) +static void free_audio_state(AudioState *s) { -AudioState *s = _audio_state; HWVoiceOut *hwo, *hwon; HWVoiceIn *hwi, *hwin; -is_cleaning_up = true; QLIST_FOREACH_SAFE(hwo, >hw_head_out, entries, hwon) { SWVoiceCap *sc; @@ -1347,6 +1349,17 @@ void audio_cleanup(void) qapi_free_Audiodev(s->dev); s->dev = NULL; } +g_free(s); +} + +void audio_cleanup(void) +{ +is_cleaning_up = true; +while (!QTAILQ_EMPTY(_states)) { +AudioState *s = QTAILQ_FIRST(_states); +QTAILQ_REMOVE(_states, s, list); +free_audio_state(s); +} } static const VMStateDescription vmstate_audio = { @@ -1373,28 +1386,33 @@ static AudiodevListEntry *audiodev_find( return NULL; } -static int audio_init(Audiodev *dev) +/* + * if we have dev, this function was called because of an -audiodev argument => + * initialize a new state with it + * if dev == NULL => legacy implicit initialization, return the already created +
[Qemu-devel] [PATCH v2 00/14] Multiple simultaneous audio backends
Hello, This is the v2 of my audio patches. Copmpared to the previous version, the only major change is the splitting of 2 commits ("basic support for multi backend audio" and "audio: audiodev= parameters no longer optional when -audiodev present") to ease review. I also included a patch that fixes a memory leak reported by ASAN. Regards, Zoltan Kővágó, Zoltán (14): audio: reduce glob_audio_state usage audio: basic support for multi backend audio audio: add audiodev property to vnc and wav_capture audio: add audiodev properties to frontends paaudio: prepare for multiple audiodev audio: audiodev= parameters no longer optional when -audiodev present paaudio: do not move stream when sink/source name is specified paaudio: properly disconnect streams in fini_* audio: remove audio_MIN, audio_MAX audio: do not run each backend in audio_run paaudio: fix playback glitches audio: remove read and write pcm_ops audio: use size_t where makes sense audio: fix memory leak reported by ASAN audio/audio.h| 36 +-- audio/audio_int.h| 43 ++-- audio/audio_template.h | 62 +++-- audio/mixeng.h | 9 +- audio/rate_template.h| 2 +- include/hw/qdev-properties.h | 3 + include/sysemu/replay.h | 4 +- ui/vnc.h | 2 + audio/alsaaudio.c| 49 ++-- audio/audio.c| 345 ++--- audio/coreaudio.c| 18 +- audio/dsoundaudio.c | 25 +- audio/noaudio.c | 39 +-- audio/ossaudio.c | 75 +++--- audio/paaudio.c | 421 ++- audio/sdlaudio.c | 30 +-- audio/spiceaudio.c | 34 +-- audio/wavaudio.c | 18 +- audio/wavcapture.c | 6 +- hw/audio/ac97.c | 11 +- hw/audio/adlib.c | 5 +- hw/audio/cs4231a.c | 5 +- hw/audio/es1370.c| 13 +- hw/audio/gus.c | 7 +- hw/audio/hda-codec.c | 17 +- hw/audio/milkymist-ac97.c| 14 +- hw/audio/pcspk.c | 3 +- hw/audio/pl041.c | 1 + hw/audio/sb16.c | 3 +- hw/audio/wm8750.c| 10 +- hw/core/qdev-properties-system.c | 57 + hw/usb/dev-audio.c | 1 + monitor/misc.c | 12 +- replay/replay-audio.c| 16 +- replay/replay.c | 2 +- ui/vnc.c | 15 +- hmp-commands.hx | 13 +- qemu-options.hx | 6 + 38 files changed, 790 insertions(+), 642 deletions(-) -- 2.22.0
[Qemu-devel] [PATCH v2 01/14] audio: reduce glob_audio_state usage
Remove glob_audio_state from functions, where possible without breaking the API. This means that most static functions in audio.c now take an AudioState pointer instead of implicitly using glob_audio_state. Also included a pointer in SWVoice*, HWVoice* structs, so that functions dealing them can know the audio state without having to pass it around separately. This is required in order to support multiple simultaneous audio backends (added in a later commit). Signed-off-by: Kővágó, Zoltán Reviewed-by: Marc-André Lureau --- audio/audio_int.h | 8 ++ audio/audio_template.h | 46 audio/audio.c | 59 +++--- 3 files changed, 57 insertions(+), 56 deletions(-) diff --git a/audio/audio_int.h b/audio/audio_int.h index 3f14842709..8164696b2c 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -49,9 +49,11 @@ struct audio_pcm_info { int swap_endianness; }; +typedef struct AudioState AudioState; typedef struct SWVoiceCap SWVoiceCap; typedef struct HWVoiceOut { +AudioState *s; int enabled; int poll_mode; int pending_disable; @@ -73,6 +75,7 @@ typedef struct HWVoiceOut { } HWVoiceOut; typedef struct HWVoiceIn { +AudioState *s; int enabled; int poll_mode; struct audio_pcm_info info; @@ -94,6 +97,7 @@ typedef struct HWVoiceIn { struct SWVoiceOut { QEMUSoundCard *card; +AudioState *s; struct audio_pcm_info info; t_sample *conv; int64_t ratio; @@ -111,6 +115,7 @@ struct SWVoiceOut { struct SWVoiceIn { QEMUSoundCard *card; +AudioState *s; int active; struct audio_pcm_info info; int64_t ratio; @@ -188,6 +193,9 @@ typedef struct AudioState { int nb_hw_voices_in; int vm_running; int64_t period_ticks; + +bool timer_running; +uint64_t timer_last; } AudioState; extern const struct mixeng_volume nominal_volume; diff --git a/audio/audio_template.h b/audio/audio_template.h index 1232bb54db..c721fed75d 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -36,9 +36,9 @@ #define HWBUF hw->conv_buf #endif -static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv) +static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, + struct audio_driver *drv) { -AudioState *s = _audio_state; int max_voices = glue (drv->max_voices_, TYPE); int voice_size = glue (drv->voice_size_, TYPE); @@ -183,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) { -AudioState *s = _audio_state; HW *hw = *hwp; +AudioState *s = hw->s; if (!hw->sw_head.lh_first) { #ifdef DAC @@ -199,15 +199,14 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) } } -static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw) +static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw) { -AudioState *s = _audio_state; return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first; } -static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw) +static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw) { -while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { +while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { if (hw->enabled) { return hw; } @@ -215,12 +214,10 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw) return NULL; } -static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( -HW *hw, -struct audsettings *as -) +static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw, + struct audsettings *as) { -while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { +while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { if (audio_pcm_info_eq (>info, as)) { return hw; } @@ -228,10 +225,10 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( return NULL; } -static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) +static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, + struct audsettings *as) { HW *hw; -AudioState *s = _audio_state; struct audio_driver *drv = s->drv; if (!glue (s->nb_hw_voices_, TYPE)) { @@ -255,6 +252,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) return NULL; } +hw->s = s; hw->pcm_ops = drv->pcm_ops; hw->ctl_caps = drv->ctl_caps; @@ -328,33 +326,33 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) abort(); } -static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) +static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as) { HW *hw; -AudioState *s = _audio_state;
[Qemu-devel] [PATCH v2 04/14] audio: add audiodev properties to frontends
Finally add audiodev= options to audio frontends so users can specify which backend to use when multiple backends exist. Not specifying an audiodev= option currently causes the first audiodev to be used, this is fixed in the next commit. Example usage: -audiodev pa,id=foo -device AC97,audiodev=foo Signed-off-by: Kővágó, Zoltán Reviewed-by: Marc-André Lureau --- audio/audio.h| 3 ++ include/hw/qdev-properties.h | 3 ++ hw/audio/ac97.c | 1 + hw/audio/adlib.c | 1 + hw/audio/cs4231a.c | 1 + hw/audio/es1370.c| 7 +++- hw/audio/gus.c | 1 + hw/audio/hda-codec.c | 1 + hw/audio/milkymist-ac97.c| 6 hw/audio/pcspk.c | 1 + hw/audio/pl041.c | 1 + hw/audio/sb16.c | 1 + hw/audio/wm8750.c| 6 hw/core/qdev-properties-system.c | 57 hw/usb/dev-audio.c | 1 + 15 files changed, 90 insertions(+), 1 deletion(-) diff --git a/audio/audio.h b/audio/audio.h index ad2457f4de..c0722a5cda 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -181,4 +181,7 @@ void audio_legacy_help(void); AudioState *audio_state_by_name(const char *name); const char *audio_get_id(QEMUSoundCard *card); +#define DEFINE_AUDIO_PROPERTIES(_s, _f) \ +DEFINE_PROP_AUDIODEV("audiodev", _s, _f) + #endif /* QEMU_AUDIO_H */ diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 1eae5ab056..de0b2c8423 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -35,6 +35,7 @@ extern const PropertyInfo qdev_prop_blocksize; extern const PropertyInfo qdev_prop_pci_host_devaddr; extern const PropertyInfo qdev_prop_uuid; extern const PropertyInfo qdev_prop_arraylen; +extern const PropertyInfo qdev_prop_audiodev; extern const PropertyInfo qdev_prop_link; extern const PropertyInfo qdev_prop_off_auto_pcibar; extern const PropertyInfo qdev_prop_pcie_link_speed; @@ -236,6 +237,8 @@ extern const PropertyInfo qdev_prop_pcie_link_width; + type_check(QemuUUID, typeof_field(_state, _field)), \ .set_default = true, \ } +#define DEFINE_PROP_AUDIODEV(_n, _s, _f) \ +DEFINE_PROP(_n, _s, _f, qdev_prop_audiodev, QEMUSoundCard) #define DEFINE_PROP_END_OF_LIST() \ {} diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index fb98da2678..0d8e524233 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1409,6 +1409,7 @@ static int ac97_init (PCIBus *bus) } static Property ac97_properties[] = { +DEFINE_AUDIO_PROPERTIES(AC97LinkState, card), DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0), DEFINE_PROP_END_OF_LIST (), }; diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 7dd9a89b89..df2e781788 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -299,6 +299,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) } static Property adlib_properties[] = { +DEFINE_AUDIO_PROPERTIES(AdlibState, card), DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220), DEFINE_PROP_UINT32 ("freq",AdlibState, freq, 44100), DEFINE_PROP_END_OF_LIST (), diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 7216b41cc1..e3ea830b47 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -689,6 +689,7 @@ static int cs4231a_init (ISABus *bus) } static Property cs4231a_properties[] = { +DEFINE_AUDIO_PROPERTIES(CSState, card), DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534), DEFINE_PROP_UINT32 ("irq", CSState, irq, 9), DEFINE_PROP_UINT32 ("dma", CSState, dma, 3), diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 260c142b70..7589671d20 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -887,6 +887,11 @@ static int es1370_init (PCIBus *bus) return 0; } +static Property es1370_properties[] = { +DEFINE_AUDIO_PROPERTIES(ES1370State, card), +DEFINE_PROP_END_OF_LIST(), +}; + static void es1370_class_init (ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS (klass); @@ -903,6 +908,7 @@ static void es1370_class_init (ObjectClass *klass, void *data) dc->desc = "ENSONIQ AudioPCI ES1370"; dc->vmsd = _es1370; dc->reset = es1370_on_reset; +dc->props = es1370_properties; } static const TypeInfo es1370_info = { @@ -923,4 +929,3 @@ static void es1370_register_types (void) } type_init (es1370_register_types) - diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 9ab51631d9..566864bc9e 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -297,6 +297,7 @@ static int GUS_init (ISABus *bus) } static Property gus_properties[] = { +DEFINE_AUDIO_PROPERTIES(GUSState, card), DEFINE_PROP_UINT32 ("freq",GUSState, freq,44100), DEFINE_PROP_UINT32 ("iobase", GUSState, port,0x240),
[Qemu-devel] [PATCH v2 03/14] audio: add audiodev property to vnc and wav_capture
Signed-off-by: Kővágó, Zoltán --- ui/vnc.h| 2 ++ monitor/misc.c | 12 +++- ui/vnc.c| 15 ++- hmp-commands.hx | 13 - qemu-options.hx | 6 ++ 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/ui/vnc.h b/ui/vnc.h index 2f84db3142..6f54653455 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -183,6 +183,8 @@ struct VncDisplay #ifdef CONFIG_VNC_SASL VncDisplaySASL sasl; #endif + +AudioState *audio_state; }; typedef struct VncTight { diff --git a/monitor/misc.c b/monitor/misc.c index e39a0e..f97810d370 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -1148,7 +1148,17 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict) int bits = qdict_get_try_int(qdict, "bits", -1); int has_channels = qdict_haskey(qdict, "nchannels"); int nchannels = qdict_get_try_int(qdict, "nchannels", -1); +const char *audiodev = qdict_get_try_str(qdict, "audiodev"); CaptureState *s; +AudioState *as = NULL; + +if (audiodev) { +as = audio_state_by_name(audiodev); +if (!as) { +monitor_printf(mon, "Invalid audiodev specified\n"); +return; +} +} s = g_malloc0 (sizeof (*s)); @@ -1156,7 +1166,7 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict) bits = has_bits ? bits : 16; nchannels = has_channels ? nchannels : 2; -if (wav_start_capture(NULL, s, path, freq, bits, nchannels)) { +if (wav_start_capture(as, s, path, freq, bits, nchannels)) { monitor_printf(mon, "Failed to add wave capture\n"); g_free (s); return; diff --git a/ui/vnc.c b/ui/vnc.c index 140f364dda..24f9be5b5d 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1222,7 +1222,7 @@ static void audio_add(VncState *vs) ops.destroy = audio_capture_destroy; ops.capture = audio_capture; -vs->audio_cap = AUD_add_capture(NULL, >as, , vs); +vs->audio_cap = AUD_add_capture(vs->vd->audio_state, >as, , vs); if (!vs->audio_cap) { error_report("Failed to add audio capture"); } @@ -3369,6 +3369,9 @@ static QemuOptsList qemu_vnc_opts = { },{ .name = "non-adaptive", .type = QEMU_OPT_BOOL, +},{ +.name = "audiodev", +.type = QEMU_OPT_STRING, }, { /* end of list */ } }, @@ -3806,6 +3809,7 @@ void vnc_display_open(const char *id, Error **errp) const char *saslauthz; int lock_key_sync = 1; int key_delay_ms; +const char *audiodev; if (!vd) { error_setg(errp, "VNC display not active"); @@ -3991,6 +3995,15 @@ void vnc_display_open(const char *id, Error **errp) } vd->ledstate = 0; +audiodev = qemu_opt_get(opts, "audiodev"); +if (audiodev) { +vd->audio_state = audio_state_by_name(audiodev); +if (!vd->audio_state) { +error_setg(errp, "Audiodev '%s' not found", audiodev); +goto fail; +} +} + device_id = qemu_opt_get(opts, "display"); if (device_id) { int head = qemu_opt_get_number(opts, "head", 0); diff --git a/hmp-commands.hx b/hmp-commands.hx index bfa5681dd2..fa7f009268 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -819,16 +819,19 @@ ETEXI { .name = "wavcapture", -.args_type = "path:F,freq:i?,bits:i?,nchannels:i?", -.params = "path [frequency [bits [channels]]]", +.args_type = "path:F,freq:i?,bits:i?,nchannels:i?,audiodev:s?", +.params = "path [frequency [bits [channels [audiodev", .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)", .cmd= hmp_wavcapture, }, STEXI -@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]] +@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels} [@var{audiodev} @findex wavcapture -Capture audio into @var{filename}. Using sample rate @var{frequency} -bits per sample @var{bits} and number of channels @var{channels}. +Capture audio into @var{filename} from @var{audiodev}, using sample rate +@var{frequency} bits per sample @var{bits} and number of channels +@var{channels}. When not using an -audiodev argument on command line, +@var{audiodev} must be omitted, otherwise is must specify a valid +audiodev. Defaults: @itemize @minus diff --git a/qemu-options.hx b/qemu-options.hx index 9621e934c0..a308e5f5aa 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1978,6 +1978,12 @@ can help the device and guest to keep up and not lose events in case events are arriving in bulk. Possible causes for the latter are flaky network connections, or scripts for automated testing. +@item audiodev=@var{audiodev} + +Use the specified @var{audiodev} when the VNC client requests audio +transmission. When not using an -audiodev argument, this option must +be omitted, otherwise is must be present and specify a valid
[Qemu-devel] [PULL 2/3] target/mips: Add missing 'break' for certain cases of MFTR handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: ead9360e2fb Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic Message-Id: <1563220847-14630-4-git-send-email-aleksandar.marko...@rt-rk.com> --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index cce1f12..b4898d5 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -9826,6 +9826,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_mfc0(ctx, t0, rt, sel); break; } +break; case 12: switch (sel) { case 0: @@ -9835,6 +9836,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_mfc0(ctx, t0, rt, sel); break; } +break; case 13: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PULL 3/3] target/mips: Add missing 'break' for certain cases of MTTR handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: ead9360e2fb Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic Message-Id: <1563220847-14630-5-git-send-email-aleksandar.marko...@rt-rk.com> --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index b4898d5..3575eff 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -10055,6 +10055,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_mtc0(ctx, t0, rd, sel); break; } +break; case 12: switch (sel) { case 0: @@ -10064,6 +10065,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_mtc0(ctx, t0, rd, sel); break; } +break; case 13: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PULL 1/3] target/mips: Add missing 'break' for a case of MTHC0 handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: 5fb2dcd1792 Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic Message-Id: <1563220847-14630-3-git-send-email-aleksandar.marko...@rt-rk.com> --- target/mips/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index f96f141..cce1f12 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -6745,6 +6745,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } +break; case CP0_REGISTER_17: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PULL 0/3] MIPS queue for July 15th, 2019
From: Aleksandar Markovic The following changes since commit 5ea8ec2fcf57cb9af24ad2cf17b4d64adb03afdf: Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-07-15' into staging (2019-07-15 16:11:47 +0100) are available in the git repository at: https://github.com/AMarkovic/qemu tags/mips-queue-jul-15-2019 for you to fetch changes up to 0d0304f2c4967c892a3216638fc4cb078afa2b44: target/mips: Add missing 'break' for certain cases of MTTR handling (2019-07-15 22:22:05 +0200) MIPS queue for July 15th, 2019 Notes: - two patches from the serieas 'MIPS fixes for 4.1' did not get reviewed on time to be included for 4.1 rc1, so they are left possibly for 4.1 rc2 Highlights: - fixes for missing 'break' in switch statements Aleksandar Markovic (3): target/mips: Add missing 'break' for a case of MTHC0 handling target/mips: Add missing 'break' for certain cases of MFTR handling target/mips: Add missing 'break' for certain cases of MTTR handling target/mips/translate.c | 5 + 1 file changed, 5 insertions(+) -- 2.7.4
[Qemu-devel] [PATCH 2/3] block/io_uring: fix EINTR request resubmission
Adding the request to sq_overflow isn't enough: 1. luringcb->sqeq is uninitialized if there was space in the sq ring at submission time. 2. Not all code paths invoke ioq_submit() after processing completions, so the request could hang. Additional bugs include checking for EINTR instead of -EINTR and forgetting to skip the completion callback when a request is resubmitted. Fix this by always initializing luringcb->sqeq and ensuring that all code paths invoke ioq_submit() after appending to sq_overflow. Ensure that luring_process_completions() marks the cqe seen and decrements in_flight before resubmitting the request. Signed-off-by: Stefan Hajnoczi --- block/io_uring.c | 64 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/block/io_uring.c b/block/io_uring.c index 19919da4c9..97e4f876d7 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -87,6 +87,18 @@ int luring_register_fd(LuringState *s, unsigned int fd) s->fd.head, s->fd.size); } +/** + * luring_resubmit: + * + * Resubmit a request by appending it to sq_overflow. The caller must ensure + * that ioq_submit() is called later so that sq_overflow requests are started. + */ +static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb) +{ +QSIMPLEQ_INSERT_TAIL(>io_q.sq_overflow, luringcb, next); +s->io_q.in_queue++; +} + /** * luring_process_completions: * @s: AIO state @@ -102,7 +114,6 @@ int luring_register_fd(LuringState *s, unsigned int fd) static void luring_process_completions(LuringState *s) { struct io_uring_cqe *cqes; -int ret; /* * Request completion callbacks can run the nested event loop. @@ -122,11 +133,20 @@ static void luring_process_completions(LuringState *s) qemu_bh_schedule(s->completion_bh); while (io_uring_peek_cqe(>ring, ) == 0) { +LuringAIOCB *luringcb; +int ret; + if (!cqes) { break; } -LuringAIOCB *luringcb = io_uring_cqe_get_data(cqes); + +luringcb = io_uring_cqe_get_data(cqes); ret = cqes->res; +io_uring_cqe_seen(>ring, cqes); +cqes = NULL; + +/* Change counters one-by-one because we can be nested. */ +s->io_q.in_flight--; trace_luring_process_completion(s, luringcb, ret); @@ -143,17 +163,12 @@ static void luring_process_completions(LuringState *s) ret = -ENOSPC;; } /* Add to overflow queue to be resubmitted later */ -} else if (ret == EINTR) { -QSIMPLEQ_INSERT_TAIL(>io_q.sq_overflow, luringcb, next); +} else if (ret == -EINTR) { +luring_resubmit(s, luringcb); +continue; } luringcb->ret = ret; - -io_uring_cqe_seen(>ring, cqes); -cqes = NULL; -/* Change counters one-by-one because we can be nested. */ -s->io_q.in_flight--; - /* * If the coroutine is already entered it must be in ioq_submit() * and will notice luringcb->ret has been filled in when it @@ -245,16 +260,16 @@ static int ioq_submit(LuringState *s) } s->io_q.in_flight += ret; s->io_q.in_queue -= ret; + +if (s->io_q.in_flight) { +/* + * We can try to complete something just right away if there are + * still requests in-flight. + */ +luring_process_completions(s); +} } s->io_q.blocked = (s->io_q.in_queue > 0); - -if (s->io_q.in_flight) { -/* - * We can try to complete something just right away if there are - * still requests in-flight. - */ -luring_process_completions(s); -} return ret; } @@ -290,15 +305,7 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, uint64_t offset, int type) { int ret; -struct io_uring_sqe *sqes = io_uring_get_sqe(>ring); -/* - *If the ring is full and cannot fetch new sqes, add the request to - * to an overflow queue to be submitted later - */ -if (!sqes) { -sqes = >sqeq; -QSIMPLEQ_INSERT_TAIL(>io_q.sq_overflow, luringcb, next); -} +struct io_uring_sqe *sqes = >sqeq; switch (type) { case QEMU_AIO_WRITE: @@ -318,7 +325,10 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, abort(); } io_uring_sqe_set_data(sqes, luringcb); + +QSIMPLEQ_INSERT_TAIL(>io_q.sq_overflow, luringcb, next); s->io_q.in_queue++; + trace_luring_do_submit(s, s->io_q.blocked, s->io_q.plugged, s->io_q.in_queue, s->io_q.in_flight); if (!s->io_q.blocked && -- 2.21.0
[Qemu-devel] [PATCH 1/3] block/io_uring: add submission and completion trace events
It is useful to follow individual requests as they are submitted. Add trace events that show details of each request. Signed-off-by: Stefan Hajnoczi --- block/io_uring.c | 5 + block/trace-events | 3 +++ 2 files changed, 8 insertions(+) diff --git a/block/io_uring.c b/block/io_uring.c index 22e8d3d9ca..19919da4c9 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -128,6 +128,8 @@ static void luring_process_completions(LuringState *s) LuringAIOCB *luringcb = io_uring_cqe_get_data(cqes); ret = cqes->res; +trace_luring_process_completion(s, luringcb, ret); + if (ret == luringcb->qiov->size) { ret = 0; } else if (ret >= 0) { @@ -233,6 +235,7 @@ static int ioq_submit(LuringState *s) QSIMPLEQ_REMOVE_HEAD(>io_q.sq_overflow, next); } ret = io_uring_submit(>ring); +trace_luring_io_uring_submit(s, ret); /* Prevent infinite loop if submission is refused */ if (ret <= 0) { if (ret == -EAGAIN) { @@ -339,6 +342,8 @@ int coroutine_fn luring_co_submit(BlockDriverState *bs, LuringState *s, int fd, .is_read= (type == QEMU_AIO_READ), }; +trace_luring_co_submit(bs, s, , fd, offset, qiov ? qiov->size : 0, type); + ret = luring_do_submit(fd, , s, offset, type); if (ret < 0) { return ret; diff --git a/block/trace-events b/block/trace-events index 069779773b..02952fe4cb 100644 --- a/block/trace-events +++ b/block/trace-events @@ -67,6 +67,9 @@ luring_io_plug(void *s) "LuringState %p plug" luring_io_unplug(void *s, int blocked, int plugged, int queued, int inflight) "LuringState %p blocked %d plugged %d queued %d inflight %d" luring_do_submit(void *s, int blocked, int plugged, int queued, int inflight) "LuringState %p blocked %d plugged %d queued %d inflight %d" luring_do_submit_done(void *s, int ret) "LuringState %p submitted to kernel %d" +luring_co_submit(void *bs, void *s, void *luringcb, int fd, uint64_t offset, size_t nbytes, int type) "bs %p s %p luringcb %p fd %d offset %" PRId64 " nbytes %zd type %d" +luring_process_completion(void *s, void *aiocb, int ret) "LuringState %p luringcb %p ret %d" +luring_io_uring_submit(void *s, int ret) "LuringState %p ret %d" # qcow2.c qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" -- 2.21.0
[Qemu-devel] [PATCH 3/3] block/io_uring: resubmit short buffered reads
The io_uring API had unusual read behavior up until recently, where short reads could occur when the start of the file range was in the page cache and a later portion was not in the page cache. Normally read(2) does not expose this detail to applications and this behavior has been fixed in Linux commit 9d93a3f5a0c ("io_uring: punt short reads to async * context"). In the meantime Linux distros have shipped kernels where io_uring exhibits the old behavior and there is no simple way to detect it. Add a slow path for resubmitting short read requests. The idea is simple: shorten the iovecs and increment the file offset each time a short read occurs and then resubmit the request. The implementation requires adding additional fields to LuringAIOCB to keep track of where we were. Signed-off-by: Stefan Hajnoczi --- block/io_uring.c | 75 +++--- block/trace-events | 3 +- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/block/io_uring.c b/block/io_uring.c index 97e4f876d7..12cef71175 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -28,6 +28,12 @@ typedef struct LuringAIOCB { QEMUIOVector *qiov; bool is_read; QSIMPLEQ_ENTRY(LuringAIOCB) next; + +/* Buffered reads may require resubmission, see + * luring_resubmit_short_read(). + */ +int total_read; +QEMUIOVector resubmit_qiov; } LuringAIOCB; typedef struct LuringQueue { @@ -99,6 +105,43 @@ static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb) s->io_q.in_queue++; } +/* Before Linux commit 9d93a3f5a0c ("io_uring: punt short reads to async + * context") a buffered I/O request with the start of the file range in the + * page cache could result in a short read. Applications need to resubmit the + * remaining read request. + * + * This is a slow path but recent kernels never take it. + */ +static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, + int nread) +{ +QEMUIOVector *resubmit_qiov; +size_t remaining; + +trace_luring_resubmit_short_read(s, luringcb, nread); + +/* Update read position */ +luringcb->total_read += nread; +remaining = luringcb->qiov->size - luringcb->total_read; + +/* Shorten qiov */ +resubmit_qiov = >resubmit_qiov; +if (resubmit_qiov->iov == NULL) { +qemu_iovec_init(resubmit_qiov, luringcb->qiov->niov); +} else { +qemu_iovec_reset(resubmit_qiov); +} +qemu_iovec_concat(resubmit_qiov, luringcb->qiov, luringcb->total_read, + remaining); + +/* Update sqe */ +luringcb->sqeq.off += nread; +luringcb->sqeq.addr = (__u64)(uintptr_t)luringcb->resubmit_qiov.iov; +luringcb->sqeq.len = luringcb->resubmit_qiov.niov; + +luring_resubmit(s, luringcb); +} + /** * luring_process_completions: * @s: AIO state @@ -135,6 +178,7 @@ static void luring_process_completions(LuringState *s) while (io_uring_peek_cqe(>ring, ) == 0) { LuringAIOCB *luringcb; int ret; +int total_bytes; if (!cqes) { break; @@ -150,25 +194,36 @@ static void luring_process_completions(LuringState *s) trace_luring_process_completion(s, luringcb, ret); -if (ret == luringcb->qiov->size) { +/* total_read is non-zero only for resubmitted read requests */ +total_bytes = ret + luringcb->total_read; + +if (ret < 0) { +if (ret == -EINTR) { +luring_resubmit(s, luringcb); +continue; +} +} else if (total_bytes == luringcb->qiov->size) { ret = 0; -} else if (ret >= 0) { +} else { /* Short Read/Write */ if (luringcb->is_read) { -/* Read, pad with zeroes */ -qemu_iovec_memset(luringcb->qiov, ret, 0, -luringcb->qiov->size - ret); -ret = 0; +if (ret > 0) { +luring_resubmit_short_read(s, luringcb, ret); +continue; +} else { +/* Pad with zeroes */ +qemu_iovec_memset(luringcb->qiov, total_bytes, 0, + luringcb->qiov->size - total_bytes); +ret = 0; +} } else { ret = -ENOSPC;; } -/* Add to overflow queue to be resubmitted later */ -} else if (ret == -EINTR) { -luring_resubmit(s, luringcb); -continue; } luringcb->ret = ret; +qemu_iovec_destroy(>resubmit_qiov); + /* * If the coroutine is already entered it must be in ioq_submit() * and will notice luringcb->ret has been filled in when it diff --git a/block/trace-events b/block/trace-events index 02952fe4cb..f434cac634 100644 --- a/block/trace-events +++ b/block/trace-events @@
[Qemu-devel] [PATCH 0/3] block/io_uring: fix EINTR and resubmit short reads
Short reads are possible with cache=writeback (see Patch 3 for details). Handle this by resubmitting requests until the read is completed. Patch 1 adds trace events useful for debugging io_uring. Patch 2 fixes EINTR. This lays the groundwork for resubmitting requests in Patch 3. Aarushi: Feel free to squash this into your patch series if you are happy with the code, I don't mind if the authorship information is lost. After applying these patches I can successfully boot a Fedora 30 guest qcow2 file with cache=writeback. Based-on: <20190610134905.22294-1-mehta.aar...@gmail.com> Stefan Hajnoczi (3): block/io_uring: add submission and completion trace events block/io_uring: fix EINTR request resubmission block/io_uring: resubmit short buffered reads block/io_uring.c | 136 ++--- block/trace-events | 6 +- 2 files changed, 108 insertions(+), 34 deletions(-) -- 2.21.0
Re: [Qemu-devel] [PATCH v4 00/18] bitmaps: introduce 'bitmap' sync mode
On 7/9/19 7:25 PM, John Snow wrote: > This series adds a new "BITMAP" sync mode that is meant to replace the > existing "INCREMENTAL" sync mode. > > This mode can have its behavior modified by issuing any of three bitmap sync > modes, passed as arguments to the job. > > The three bitmap sync modes are: > - ON-SUCCESS: This is an alias for the old incremental mode. The bitmap is > conditionally synchronized based on the return code of the job > upon completion. > - NEVER: This is, effectively, the differential backup mode. It never clears > the bitmap, as the name suggests. > - ALWAYS: Here is the new, exciting thing. The bitmap is always synchronized, > even on failure. On success, this is identical to incremental, but > on failure it clears only the bits that were copied successfully. > This can be used to "resume" incremental backups from later points > in times. > > I wrote this series by accident on my way to implement incremental mode > for mirror, but this happened first -- the problem is that Mirror mode > uses its existing modes in a very particular way; and this was the best > way to add bitmap support into the mirror job properly. > > Summary: > - 01-03: refactor blockdev-backup and drive-backup to share more interface > code > - 04-05: add the new 'bitmap' sync mode with sync policy 'conditional', > which is functionally identical to 'incremental' sync mode. > - 06:add sync policy 'never' ("Differential" backups.) > - 07-11: rework some merging code to facilite patch 12; > - 12:add sync policy 'always' ("Resumable" backups) > - 13-16: test infrastructure changes to support patch 16: > - 17:new iotest! > - 18:minor policy loosening as a QOL improvement > > Future work: > - Update bitmaps.rst to explain these. (WIP, it's hard, sorry!) > - Add these modes to Mirror. (Done*, but needs tests.) > - Allow the use of bitmaps and bitmap sync modes with non-BITMAP modes; >This will allow for resumable/re-tryable full backups. > > === > V4: > === > > [] : patches are identical > [] : number of functional differences between upstream/downstream patch > [down] : patch is downstream-only > The flags [FC] indicate (F)unctional and (C)ontextual differences, > respectively > > 001/18:[] [--] 'qapi/block-core: Introduce BackupCommon' > 002/18:[] [--] 'drive-backup: create do_backup_common' > 003/18:[] [--] 'blockdev-backup: utilize do_backup_common' > 004/18:[] [--] 'qapi: add BitmapSyncMode enum' > 005/18:[] [--] 'block/backup: Add mirror sync mode 'bitmap'' > 006/18:[] [--] 'block/backup: add 'never' policy to bitmap sync mode' > 007/18:[] [--] 'hbitmap: Fix merge when b is empty, and result is not an > alias of a' > 008/18:[] [--] 'hbitmap: enable merging across granularities' > 009/18:[0004] [FC] 'block/dirty-bitmap: add bdrv_dirty_bitmap_merge_internal' > 010/18:[] [--] 'block/dirty-bitmap: add bdrv_dirty_bitmap_get' > 011/18:[0008] [FC] 'block/backup: upgrade copy_bitmap to BdrvDirtyBitmap' > 012/18:[] [--] 'block/backup: add 'always' bitmap sync policy' > 013/18:[] [--] 'iotests: add testing shim for script-style python tests' > 014/18:[] [--] 'iotests: teach run_job to cancel pending jobs' > 015/18:[] [--] 'iotests: teach FilePath to produce multiple paths' > 016/18:[] [--] 'iotests: Add virtio-scsi device helper' > 017/18:[0063] [FC] 'iotests: add test 257 for bitmap-mode backups' > 018/18:[] [--] 'block/backup: loosen restriction on readonly bitmaps' > > Changes: > 009: Added assertions. > 011: Moved copy bitmap to source node. > 017: Rework get_bitmap to tolerate multiple anonymous bitmaps > Update test output to accommodate the same. > > === > V3: > === > > Changes: > 001: Made suggested doc fixes. > Changed 'since' to 4.2. > 002: Added bds and aio_context to backup_common > Removed accidental extraneous unref on target_bs > Removed local_err propagation > 003: Fallout from #002; hoist aio_context acquisition up into > do_blockdev_backup > 004: 'conditional' --> 'on-success' > 005: Rediscover the lost stanza that ensures a bitmap mode was given > Fallout from 2, 3, 4. > 006: Block comment fix for patchew > Fallout from #4 > 009: Fix assert() style issue. Why'd they let a macro be lowercase like that? > Probably to make specifically my life difficult. > 010: Fix style issue { > 011: Fix long lines > rename "bs" --> "target_bs" where appropriate > Free copy_bitmap from the right node > 012: Multiline comment changes for patchew > Fallout from #4 > 015: Fix long line for patchew > Reinstate that second newline that Max likes > 017: Fallout from #4. > > === > V2: > === > > Changes: > 004: Fixed typo > Change @conditional docstring > 005: Moved desugaring code into blockdev.c, facilitated by patches 1-3. > 006: Change @never docstring slightly. > 007:
[Qemu-devel] [PATCH for 4.1 v4 0/5] target/mips: Fixes for 4.1 rc1
From: Aleksandar Markovic At the moment, this includes fixes for problems in switch statements found by GCC 8.3 improved code analysis features, and one big endian host fix. v3->v4: - complete the last patch v2->v3: - fix handling of MSA pack instructions on big endian host v1->v2: - excluded the patch on "ucontext" that will go into linux user queue Aleksandar Markovic (5): target/mips: Add 'fall through' comments for handling nanoMips' SHXS, SWXS target/mips: Add missing 'break' for a case of MTHC0 handling target/mips: Add missing 'break' for certain cases of MFTR handling target/mips: Add missing 'break' for certain cases of MTTR handling target/mips: Fix emulation of MSA pack instructions on big endian hosts target/mips/msa_helper.c | 74 target/mips/translate.c | 7 + 2 files changed, 81 insertions(+) -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v4 3/5] target/mips: Add missing 'break' for certain cases of MFTR handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: ead9360e2fb Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index 59d4acd..b1cf5f0 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -9826,6 +9826,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_mfc0(ctx, t0, rt, sel); break; } +break; case 12: switch (sel) { case 0: @@ -9835,6 +9836,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_mfc0(ctx, t0, rt, sel); break; } +break; case 13: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v4 5/5] target/mips: Fix emulation of MSA pack instructions on big endian hosts
From: Aleksandar Markovic Fix emulation of MSA pack instructions on big endian hosts. Signed-off-by: Aleksandar Markovic --- target/mips/msa_helper.c | 74 1 file changed, 74 insertions(+) diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c index a383c40..27560ac 100644 --- a/target/mips/msa_helper.c +++ b/target/mips/msa_helper.c @@ -2113,6 +2113,24 @@ void helper_msa_pckev_df(CPUMIPSState *env, uint32_t df, uint32_t wd, switch (df) { case DF_BYTE: +#if defined(HOST_WORDS_BIGENDIAN) +pwd->b[8] = pws->b[9]; +pwd->b[10] = pws->b[13]; +pwd->b[12] = pws->b[1]; +pwd->b[14] = pws->b[5]; +pwd->b[0] = pwt->b[9]; +pwd->b[2] = pwt->b[13]; +pwd->b[4] = pwt->b[1]; +pwd->b[6] = pwt->b[5]; +pwd->b[9] = pws->b[11]; +pwd->b[13] = pws->b[3]; +pwd->b[1] = pwt->b[11]; +pwd->b[5] = pwt->b[3]; +pwd->b[11] = pws->b[15]; +pwd->b[3] = pwt->b[15]; +pwd->b[15] = pws->b[7]; +pwd->b[7] = pwt->b[7]; +#else pwd->b[15] = pws->b[14]; pwd->b[13] = pws->b[10]; pwd->b[11] = pws->b[6]; @@ -2129,8 +2147,19 @@ void helper_msa_pckev_df(CPUMIPSState *env, uint32_t df, uint32_t wd, pwd->b[4] = pwt->b[8]; pwd->b[8] = pws->b[0]; pwd->b[0] = pwt->b[0]; +#endif break; case DF_HALF: +#if defined(HOST_WORDS_BIGENDIAN) +pwd->h[4] = pws->h[5]; +pwd->h[6] = pws->h[1]; +pwd->h[0] = pwt->h[5]; +pwd->h[2] = pwt->h[1]; +pwd->h[5] = pws->h[7]; +pwd->h[1] = pwt->h[7]; +pwd->h[7] = pws->h[3]; +pwd->h[3] = pwt->h[3]; +#else pwd->h[7] = pws->h[6]; pwd->h[5] = pws->h[2]; pwd->h[3] = pwt->h[6]; @@ -2139,12 +2168,20 @@ void helper_msa_pckev_df(CPUMIPSState *env, uint32_t df, uint32_t wd, pwd->h[2] = pwt->h[4]; pwd->h[4] = pws->h[0]; pwd->h[0] = pwt->h[0]; +#endif break; case DF_WORD: +#if defined(HOST_WORDS_BIGENDIAN) +pwd->w[2] = pws->w[3]; +pwd->w[0] = pwt->w[3]; +pwd->w[3] = pws->w[1]; +pwd->w[1] = pwt->w[1]; +#else pwd->w[3] = pws->w[2]; pwd->w[1] = pwt->w[2]; pwd->w[2] = pws->w[0]; pwd->w[0] = pwt->w[0]; +#endif break; case DF_DOUBLE: pwd->d[1] = pws->d[0]; @@ -2164,6 +2201,24 @@ void helper_msa_pckod_df(CPUMIPSState *env, uint32_t df, uint32_t wd, switch (df) { case DF_BYTE: +#if defined(HOST_WORDS_BIGENDIAN) +pwd->b[7] = pws->b[6]; +pwd->b[5] = pws->b[2]; +pwd->b[3] = pws->b[14]; +pwd->b[1] = pws->b[10]; +pwd->b[15] = pwt->b[6]; +pwd->b[13] = pwt->b[2]; +pwd->b[11] = pwt->b[14]; +pwd->b[9] = pwt->b[10]; +pwd->b[6] = pws->b[4]; +pwd->b[2] = pws->b[12]; +pwd->b[14] = pwt->b[4]; +pwd->b[10] = pwt->b[12]; +pwd->b[4] = pws->b[0]; +pwd->b[12] = pwt->b[0]; +pwd->b[0] = pws->b[8]; +pwd->b[8] = pwt->b[8]; +#else pwd->b[0] = pwt->b[1]; pwd->b[2] = pwt->b[5]; pwd->b[4] = pwt->b[9]; @@ -2180,8 +2235,19 @@ void helper_msa_pckod_df(CPUMIPSState *env, uint32_t df, uint32_t wd, pwd->b[11] = pws->b[7]; pwd->b[7] = pwt->b[15]; pwd->b[15] = pws->b[15]; +#endif break; case DF_HALF: +#if defined(HOST_WORDS_BIGENDIAN) +pwd->h[3] = pws->h[2]; +pwd->h[1] = pws->h[6]; +pwd->h[7] = pwt->h[2]; +pwd->h[5] = pwt->h[6]; +pwd->h[2] = pws->h[0]; +pwd->h[6] = pwt->h[0]; +pwd->h[0] = pws->h[4]; +pwd->h[4] = pwt->h[4]; +#else pwd->h[0] = pwt->h[1]; pwd->h[2] = pwt->h[5]; pwd->h[4] = pws->h[1]; @@ -2190,12 +2256,20 @@ void helper_msa_pckod_df(CPUMIPSState *env, uint32_t df, uint32_t wd, pwd->h[5] = pws->h[3]; pwd->h[3] = pwt->h[7]; pwd->h[7] = pws->h[7]; +#endif break; case DF_WORD: +#if defined(HOST_WORDS_BIGENDIAN) +pwd->w[1] = pws->w[0]; +pwd->w[3] = pwt->w[0]; +pwd->w[0] = pws->w[2]; +pwd->w[2] = pwt->w[2]; +#else pwd->w[0] = pwt->w[1]; pwd->w[2] = pws->w[1]; pwd->w[1] = pwt->w[3]; pwd->w[3] = pws->w[3]; +#endif break; case DF_DOUBLE: pwd->d[0] = pwt->d[1]; -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v4 1/5] target/mips: Add 'fall through' comments for handling nanoMips' SHXS, SWXS
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index f96f141..2be5e2d 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20136,12 +20136,14 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) switch (extract32(ctx->opcode, 7, 4)) { case NM_SHXS: check_nms(ctx); +/* fall through */ case NM_LHXS: case NM_LHUXS: tcg_gen_shli_tl(t0, t0, 1); break; case NM_SWXS: check_nms(ctx); +/* fall through */ case NM_LWXS: case NM_LWC1XS: case NM_SWC1XS: -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v4 2/5] target/mips: Add missing 'break' for a case of MTHC0 handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: 5fb2dcd1792 Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index 2be5e2d..59d4acd 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -6745,6 +6745,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } +break; case CP0_REGISTER_17: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v4 4/5] target/mips: Add missing 'break' for certain cases of MTTR handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: ead9360e2fb Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index b1cf5f0..ca62800 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -10055,6 +10055,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_mtc0(ctx, t0, rd, sel); break; } +break; case 12: switch (sel) { case 0: @@ -10064,6 +10065,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_mtc0(ctx, t0, rd, sel); break; } +break; case 13: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v3 5/5] target/mips: Fix emulation of MSA pack instructions on big endian hosts
From: Aleksandar Markovic Fix emulation of MSA pack instructions on big endian hosts. Signed-off-by: Aleksandar Markovic --- target/mips/msa_helper.c | 66 1 file changed, 66 deletions(-) diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c index a383c40..4098842 100644 --- a/target/mips/msa_helper.c +++ b/target/mips/msa_helper.c @@ -2113,39 +2113,6 @@ void helper_msa_pckev_df(CPUMIPSState *env, uint32_t df, uint32_t wd, switch (df) { case DF_BYTE: -pwd->b[15] = pws->b[14]; -pwd->b[13] = pws->b[10]; -pwd->b[11] = pws->b[6]; -pwd->b[9] = pws->b[2]; -pwd->b[7] = pwt->b[14]; -pwd->b[5] = pwt->b[10]; -pwd->b[3] = pwt->b[6]; -pwd->b[1] = pwt->b[2]; -pwd->b[14] = pws->b[12]; -pwd->b[10] = pws->b[4]; -pwd->b[6] = pwt->b[12]; -pwd->b[2] = pwt->b[4]; -pwd->b[12] = pws->b[8]; -pwd->b[4] = pwt->b[8]; -pwd->b[8] = pws->b[0]; -pwd->b[0] = pwt->b[0]; -break; -case DF_HALF: -pwd->h[7] = pws->h[6]; -pwd->h[5] = pws->h[2]; -pwd->h[3] = pwt->h[6]; -pwd->h[1] = pwt->h[2]; -pwd->h[6] = pws->h[4]; -pwd->h[2] = pwt->h[4]; -pwd->h[4] = pws->h[0]; -pwd->h[0] = pwt->h[0]; -break; -case DF_WORD: -pwd->w[3] = pws->w[2]; -pwd->w[1] = pwt->w[2]; -pwd->w[2] = pws->w[0]; -pwd->w[0] = pwt->w[0]; -break; case DF_DOUBLE: pwd->d[1] = pws->d[0]; pwd->d[0] = pwt->d[0]; @@ -2164,39 +2131,6 @@ void helper_msa_pckod_df(CPUMIPSState *env, uint32_t df, uint32_t wd, switch (df) { case DF_BYTE: -pwd->b[0] = pwt->b[1]; -pwd->b[2] = pwt->b[5]; -pwd->b[4] = pwt->b[9]; -pwd->b[6] = pwt->b[13]; -pwd->b[8] = pws->b[1]; -pwd->b[10] = pws->b[5]; -pwd->b[12] = pws->b[9]; -pwd->b[14] = pws->b[13]; -pwd->b[1] = pwt->b[3]; -pwd->b[5] = pwt->b[11]; -pwd->b[9] = pws->b[3]; -pwd->b[13] = pws->b[11]; -pwd->b[3] = pwt->b[7]; -pwd->b[11] = pws->b[7]; -pwd->b[7] = pwt->b[15]; -pwd->b[15] = pws->b[15]; -break; -case DF_HALF: -pwd->h[0] = pwt->h[1]; -pwd->h[2] = pwt->h[5]; -pwd->h[4] = pws->h[1]; -pwd->h[6] = pws->h[5]; -pwd->h[1] = pwt->h[3]; -pwd->h[5] = pws->h[3]; -pwd->h[3] = pwt->h[7]; -pwd->h[7] = pws->h[7]; -break; -case DF_WORD: -pwd->w[0] = pwt->w[1]; -pwd->w[2] = pws->w[1]; -pwd->w[1] = pwt->w[3]; -pwd->w[3] = pws->w[3]; -break; case DF_DOUBLE: pwd->d[0] = pwt->d[1]; pwd->d[1] = pws->d[1]; -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v3 4/5] target/mips: Add missing 'break' for certain cases of MTTR handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: ead9360e2fb Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index b1cf5f0..ca62800 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -10055,6 +10055,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_mtc0(ctx, t0, rd, sel); break; } +break; case 12: switch (sel) { case 0: @@ -10064,6 +10065,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_mtc0(ctx, t0, rd, sel); break; } +break; case 13: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v3 0/5] target/mips: Fixes for 4.1 rc1
From: Aleksandar Markovic At the moment, this includes fixes for problems in switch statements found by GCC 8.3 improved code analysis features, and one big endian host fix. v2->v3: - fix handling of MSA pack instructions on big endian host v1->v2: - excluded the patch on "ucontext" that will go into linux user queue Aleksandar Markovic (5): target/mips: Add 'fall through' comments for handling nanoMips' SHXS, SWXS target/mips: Add missing 'break' for a case of MTHC0 handling target/mips: Add missing 'break' for certain cases of MFTR handling target/mips: Add missing 'break' for certain cases of MTTR handling target/mips: Fix emulation of MSA pack instructions on big endian hosts target/mips/msa_helper.c | 66 target/mips/translate.c | 7 + 2 files changed, 7 insertions(+), 66 deletions(-) -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v3 3/5] target/mips: Add missing 'break' for certain cases of MFTR handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: ead9360e2fb Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index 59d4acd..b1cf5f0 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -9826,6 +9826,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_mfc0(ctx, t0, rt, sel); break; } +break; case 12: switch (sel) { case 0: @@ -9835,6 +9836,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_mfc0(ctx, t0, rt, sel); break; } +break; case 13: switch (sel) { case 0: -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v3 1/5] target/mips: Add 'fall through' comments for handling nanoMips' SHXS, SWXS
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index f96f141..2be5e2d 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -20136,12 +20136,14 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) switch (extract32(ctx->opcode, 7, 4)) { case NM_SHXS: check_nms(ctx); +/* fall through */ case NM_LHXS: case NM_LHUXS: tcg_gen_shli_tl(t0, t0, 1); break; case NM_SWXS: check_nms(ctx); +/* fall through */ case NM_LWXS: case NM_LWC1XS: case NM_SWC1XS: -- 2.7.4
[Qemu-devel] [PATCH for 4.1 v3 2/5] target/mips: Add missing 'break' for a case of MTHC0 handling
From: Aleksandar Markovic This was found by GCC 8.3 static analysis. Fixes: 5fb2dcd1792 Reported-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Aleksandar Markovic --- target/mips/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/mips/translate.c b/target/mips/translate.c index 2be5e2d..59d4acd 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -6745,6 +6745,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } +break; case CP0_REGISTER_17: switch (sel) { case 0: -- 2.7.4
Re: [Qemu-devel] [PATCH v3 0/3] qapi: block-dirty-bitmap-remove transaction action
On 7/8/19 6:04 PM, John Snow wrote: > Hi, this is a proposal based off of Vladimir's patchset: > [Qemu-devel] [PATCH 0/4] qapi: block-dirty-bitmap-remove transaction action > > === > V3: > === > > 001/3:[] [--] 'blockdev: reduce aio_context locked sections in bitmap > add/remove' > 002/3:[0024] [FC] 'qapi: implement block-dirty-bitmap-remove transaction > action' > 003/3:[] [--] 'iotests: test bitmap moving inside 254' > > - Changed "squelch_persistence" to "skip_store" > - Use Max's suggestion for return expr > > === > V2: > === > > It replaces patches two and three with a modified patch (now patch 2) > that foregoes the need for a hide()/unhide() bitmap API. I think it's > suitable as a smaller alternative, but I'm not sure if it covers all > of the use cases of the original series. > > Patches 1 and 3 (formerly 4) included as-is. > > John Snow (1): > qapi: implement block-dirty-bitmap-remove transaction action > > Vladimir Sementsov-Ogievskiy (2): > blockdev: reduce aio_context locked sections in bitmap add/remove > iotests: test bitmap moving inside 254 > > block.c| 2 +- > block/dirty-bitmap.c | 15 +++-- > blockdev.c | 105 ++--- > include/block/dirty-bitmap.h | 2 +- > migration/block-dirty-bitmap.c | 2 +- > qapi/transaction.json | 2 + > tests/qemu-iotests/254 | 30 +- > tests/qemu-iotests/254.out | 82 + > 8 files changed, 206 insertions(+), 34 deletions(-) > Thanks, applied to my bitmaps tree: https://github.com/jnsnow/qemu/commits/bitmaps https://github.com/jnsnow/qemu.git --js (Vladimir: if this isn't amenable to you, it's going in for 4.2, so we have until the next freeze to change it. Let me know, OK?)
Re: [Qemu-devel] [BUG] nanoMIPS support problem related to extract2 support for i386 TCG target
On Sat, Jul 13, 2019 at 9:21 AM Alex Bennée wrote: > > Please see the fix: > > Subject: [PATCH for-4.1] tcg: Fix constant folding of INDEX_op_extract2_i32 > Date: Tue, 9 Jul 2019 14:19:00 +0200 > Message-Id: <20190709121900.25644-1-richard.hender...@linaro.org> > Thanks, this fixed the behavior. Sincerely, Aleksandar > > > > > Yours, > > Aleksandar > > > -- > Alex Bennée >
[Qemu-devel] [PATCH v5 20/20] hmp: call the asynchronous QMP screendump to fix outdated/glitches
In order to fix the bad screendumps (same as rhbz#1230527), call into the asynchonous version of the QMP command. Signed-off-by: Marc-André Lureau --- hmp-commands.hx | 3 ++- include/ui/console.h | 5 ++--- monitor/hmp-cmds.c | 6 ++ ui/console.c | 24 +--- 4 files changed, 7 insertions(+), 31 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index bfa5681dd2..fa559d6a4e 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -278,7 +278,8 @@ ETEXI .params = "filename [device [head]]", .help = "save screen from head 'head' of display device 'device' " "into PPM image 'filename'", -.cmd= hmp_screendump, +.async_cmd = hmp_screendump_async, +.async = true, }, STEXI diff --git a/include/ui/console.h b/include/ui/console.h index a1935557cc..d0a2a2066f 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -6,6 +6,7 @@ #include "qemu/notify.h" #include "qemu/error-report.h" #include "qapi/qapi-types-ui.h" +#include "qapi/qmp/dispatch.h" #ifdef CONFIG_OPENGL # include @@ -74,9 +75,7 @@ typedef struct MouseTransformInfo { } MouseTransformInfo; void hmp_mouse_set(Monitor *mon, const QDict *qdict); -void hmp_screendump_sync(const char *filename, - bool has_device, const char *device, - bool has_head, int64_t head, Error **errp); +void hmp_screendump_async(Monitor *mon, const QDict *qdict, QmpReturn *qret); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx constants) */ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 50a25a1ddc..e2af0e6307 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -2334,15 +2334,13 @@ err_out: goto out; } -void hmp_screendump(Monitor *mon, const QDict *qdict) +void hmp_screendump_async(Monitor *mon, const QDict *qdict, QmpReturn *qret) { const char *filename = qdict_get_str(qdict, "filename"); const char *id = qdict_get_try_str(qdict, "device"); int64_t head = qdict_get_try_int(qdict, "head", 0); -Error *err = NULL; -hmp_screendump_sync(filename, id != NULL, id, id != NULL, head, ); -hmp_handle_error(mon, ); +qmp_screendump(filename, id != NULL, id, id != NULL, head, qret); } void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) diff --git a/ui/console.c b/ui/console.c index 29c850c31c..7436b153b9 100644 --- a/ui/console.c +++ b/ui/console.c @@ -280,7 +280,7 @@ static void qmp_screendump_finish(QemuConsole *con, struct qmp_screendump *dump) goto cleanup; } -cur_mon = qmp_return_get_monitor(dump->ret); +cur_mon = qmp_return_get_monitor(dump->ret, true); surface = qemu_console_surface(con); if (!surface) { error_setg(, "no surface"); @@ -430,28 +430,6 @@ static QemuConsole *get_console(bool has_device, const char *device, return con; } -void hmp_screendump_sync(const char *filename, - bool has_device, const char *device, - bool has_head, int64_t head, Error **errp) -{ -DisplaySurface *surface; -QemuConsole *con = get_console(has_device, device, has_head, head, errp); - -if (!con) { -return; -} -/* This may not complete the drawing with Spice, you may have - * glitches or outdated dumps, use qmp instead! */ -graphic_hw_update(con); -surface = qemu_console_surface(con); -if (!surface) { -error_setg(errp, "no surface"); -return; -} - -ppm_save(filename, surface, errp); -} - void qmp_screendump(const char *filename, bool has_device, const char *device, bool has_head, int64_t head, -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 18/20] monitor: start making qmp_human_monitor_command() asynchronous
This prepares the work for HMP commands to be asynchronous. Start making QMP human-monitor-command asynchronous, although QmpReturn is used synchronously on error or after handle_hmp_command(). Signed-off-by: Marc-André Lureau --- monitor/misc.c | 14 -- qapi/misc.json | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/monitor/misc.c b/monitor/misc.c index a23c1b8ba4..0645667e1b 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -115,8 +115,8 @@ static QLIST_HEAD(, MonFdset) mon_fdsets; static HMPCommand hmp_info_cmds[]; -char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, -int64_t cpu_index, Error **errp) +void qmp_human_monitor_command(const char *command_line, bool has_cpu_index, + int64_t cpu_index, QmpReturn *qret) { char *output = NULL; Monitor *old_mon; @@ -130,15 +130,15 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, if (has_cpu_index) { int ret = monitor_set_cpu(cpu_index); if (ret < 0) { -cur_mon = old_mon; -error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", +Error *err = NULL; +error_setg(, QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number"); +qmp_return_error(qret, err); goto out; } } handle_hmp_command(, command_line); -cur_mon = old_mon; qemu_mutex_lock(_lock); if (qstring_get_length(hmp.common.outbuf) > 0) { @@ -148,9 +148,11 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, } qemu_mutex_unlock(_lock); +qmp_human_monitor_command_return(qret, output); + out: +cur_mon = old_mon; monitor_data_destroy(); -return output; } /** diff --git a/qapi/misc.json b/qapi/misc.json index a7fba7230c..8c6ca46b8b 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -1047,7 +1047,8 @@ ## { 'command': 'human-monitor-command', 'data': {'command-line': 'str', '*cpu-index': 'int'}, - 'returns': 'str' } + 'returns': 'str', + 'async': true } ## # @change: -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 19/20] monitor: teach HMP about asynchronous commands
Similar to how we handle both synchronous and asynchronous commands in QMP, HMP gains a new async_cmd() that will allow the command to complete asynchronously. For interactive reasons, and command ordering, the HMP monitor is suspended until the asynchronous command completes. It is expected that HMP async commands will be implemented re-using QMP async commands counterparts, so it reuses the QmpSession/QmpReturn for context handling (instead of introducing HmpSession/HmpReturn and having to convert from one to the other as we call QMP counterparts). hmp_dispatch_return_cb() will handle printing the result to the current monitor. It may have different ways to print the QmpReturn result to the current monitor. Currently, only error reporting is implemented. QMP human-monitor-command is modified to deal with an async HMP commands too. It creates a temporary session, and the return callback will return asynchronously to the original QMP command and destroy the temporary monitor when hmp->for_qmp_command is set. Signed-off-by: Marc-André Lureau --- include/monitor/monitor.h | 2 +- monitor/hmp.c | 110 +++-- monitor/misc.c | 40 -- monitor/monitor-internal.h | 9 ++- monitor/qmp.c | 14 +++-- 5 files changed, 123 insertions(+), 52 deletions(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 6a2907a366..5968b52fe2 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -44,6 +44,6 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd); void monitor_fdset_dup_fd_remove(int dup_fd); int64_t monitor_fdset_dup_fd_find(int dup_fd); -Monitor *qmp_return_get_monitor(QmpReturn *qret); +Monitor *qmp_return_get_monitor(QmpReturn *qret, bool hmp); #endif /* MONITOR_H */ diff --git a/monitor/hmp.c b/monitor/hmp.c index 5223661e82..b2b6dce8fb 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -26,11 +26,15 @@ #include #include "monitor-internal.h" #include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qmp/qerror.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" #include "qemu/config-file.h" #include "qemu/ctype.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/option.h" #include "qemu/units.h" @@ -38,6 +42,8 @@ #include "sysemu/sysemu.h" #include "trace.h" +static bool handle_hmp_command(MonitorHMP *mon, const char *cmdline); + static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque) { @@ -1056,7 +1062,7 @@ fail: return NULL; } -void handle_hmp_command(MonitorHMP *mon, const char *cmdline) +static bool handle_hmp_command(MonitorHMP *mon, const char *cmdline) { QDict *qdict; const HMPCommand *cmd; @@ -1066,7 +1072,7 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) cmd = monitor_parse_command(mon, cmdline, , hmp_cmds); if (!cmd) { -return; +return false; } qdict = monitor_parse_arguments(>common, , cmd); @@ -1076,11 +1082,19 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) } monitor_printf(>common, "Try \"help %.*s\" for more information\n", (int)(cmdline - cmd_start), cmd_start); -return; +return false; } -cmd->cmd(>common, qdict); +if (cmd->async) { +QmpReturn *qret = qmp_return_new(>qmp_session, NULL); +monitor_suspend(>common); +cmd->async_cmd(>common, qdict, qret); +} else { +cmd->cmd(>common, qdict); +} qobject_unref(qdict); + +return cmd->async; } static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) @@ -1395,6 +1409,59 @@ static void monitor_readline_flush(void *opaque) monitor_flush(>common); } +static void free_hmp_monitor(void *opaque) +{ +MonitorHMP *hmp = opaque; + +qmp_session_destroy(>qmp_session); +monitor_data_destroy(>common); +g_free(hmp); +} + +static AioContext *monitor_get_aio_context(void) +{ +return iothread_get_aio_context(mon_iothread); +} + +static void qmp_human_monitor_command_finish(MonitorHMP *hmp, QmpReturn *qret) +{ +char *output; + +qemu_mutex_lock(>common.mon_lock); +if (qstring_get_length(hmp->common.outbuf) > 0) { +output = g_strdup(qstring_get_str(hmp->common.outbuf)); +} else { +output = g_strdup(""); +} +qemu_mutex_unlock(>common.mon_lock); + +qmp_human_monitor_command_return(qret, output); + +if (hmp->for_qmp_command) { +aio_bh_schedule_oneshot(monitor_get_aio_context(), +free_hmp_monitor, hmp); +} +} + +static void hmp_dispatch_return_cb(QmpSession *session, QDict *rsp) +{ +MonitorHMP *hmp = container_of(session, MonitorHMP, qmp_session); +QDict *err = qdict_get_qdict(rsp,
[Qemu-devel] [PATCH v5 17/20] console: make screendump asynchronous
Make screendump asynchronous to provide correct screendumps. For now, HMP doesn't have async support, so it has to remain synchronous and potentially incorrect to avoid races (following patches will add HMP asynchronous commands) Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1230527 Signed-off-by: Marc-André Lureau --- include/ui/console.h | 3 ++ monitor/hmp-cmds.c | 2 +- qapi/ui.json | 3 +- ui/console.c | 103 +++ 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 281f9c145b..a1935557cc 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -74,6 +74,9 @@ typedef struct MouseTransformInfo { } MouseTransformInfo; void hmp_mouse_set(Monitor *mon, const QDict *qdict); +void hmp_screendump_sync(const char *filename, + bool has_device, const char *device, + bool has_head, int64_t head, Error **errp); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx constants) */ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 5ca3ebe942..50a25a1ddc 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -2341,7 +2341,7 @@ void hmp_screendump(Monitor *mon, const QDict *qdict) int64_t head = qdict_get_try_int(qdict, "head", 0); Error *err = NULL; -qmp_screendump(filename, id != NULL, id, id != NULL, head, ); +hmp_screendump_sync(filename, id != NULL, id, id != NULL, head, ); hmp_handle_error(mon, ); } diff --git a/qapi/ui.json b/qapi/ui.json index 59e412139a..cbb3979172 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -96,7 +96,8 @@ # ## { 'command': 'screendump', - 'data': {'filename': 'str', '*device': 'str', '*head': 'int'} } + 'data': {'filename': 'str', '*device': 'str', '*head': 'int'}, + 'async': true } ## # == Spice diff --git a/ui/console.c b/ui/console.c index 3c941528d2..29c850c31c 100644 --- a/ui/console.c +++ b/ui/console.c @@ -33,6 +33,7 @@ #include "chardev/char-fe.h" #include "trace.h" #include "exec/memory.h" +#include "monitor/monitor.h" #define DEFAULT_BACKSCROLL 512 #define CONSOLE_CURSOR_PERIOD 500 @@ -117,6 +118,12 @@ typedef enum { TEXT_CONSOLE_FIXED_SIZE } console_type_t; +struct qmp_screendump { +gchar *filename; +QmpReturn *ret; +QLIST_ENTRY(qmp_screendump) link; +}; + struct QemuConsole { Object parent; @@ -167,6 +174,8 @@ struct QemuConsole { uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; +QLIST_HEAD(, qmp_screendump) qmp_screendumps; + QTAILQ_ENTRY(QemuConsole) next; }; @@ -193,6 +202,8 @@ static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); +static void ppm_save(const char *filename, DisplaySurface *ds, + Error **errp); static void gui_update(void *opaque) { @@ -259,8 +270,48 @@ static void gui_setup_refresh(DisplayState *ds) ds->have_text = have_text; } +static void qmp_screendump_finish(QemuConsole *con, struct qmp_screendump *dump) +{ +Error *err = NULL; +DisplaySurface *surface; +Monitor *prev_mon = cur_mon; + +if (qmp_return_is_cancelled(dump->ret)) { +goto cleanup; +} + +cur_mon = qmp_return_get_monitor(dump->ret); +surface = qemu_console_surface(con); +if (!surface) { +error_setg(, "no surface"); +} else { +/* + * FIXME: async save with coroutine? it would have to copy or + * lock the surface. + */ +ppm_save(dump->filename, surface, ); +} + +if (err) { +qmp_return_error(dump->ret, err); +} else { +qmp_screendump_return(dump->ret); +} +cur_mon = prev_mon; + +cleanup: +g_free(dump->filename); +QLIST_REMOVE(dump, link); +g_free(dump); +} + void graphic_hw_update_done(QemuConsole *con) { +struct qmp_screendump *dump, *next; + +QLIST_FOREACH_SAFE(dump, >qmp_screendumps, link, next) { +qmp_screendump_finish(con, dump); +} } void graphic_hw_update(QemuConsole *con) @@ -356,30 +407,41 @@ write_err: goto out; } -void qmp_screendump(const char *filename, bool has_device, const char *device, -bool has_head, int64_t head, Error **errp) + +static QemuConsole *get_console(bool has_device, const char *device, +bool has_head, int64_t head, Error **errp) { -QemuConsole *con; -DisplaySurface *surface; +QemuConsole *con = NULL; if (has_device) { con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, errp); -if (!con) { -return; -} } else { if (has_head) { error_setg(errp, "'head' must be specified together with 'device'"); -
[Qemu-devel] [PATCH v5 16/20] console: add graphic_hw_update_done()
Add a function to be called when a graphic update is done. Declare the QXL renderer as async: render_update_cookie_num counts the number of outstanding updates, and graphic_hw_update_done() is called when it reaches none. Signed-off-by: Marc-André Lureau Reviewed-by: Gerd Hoffmann --- hw/display/qxl-render.c | 9 +++-- hw/display/qxl.c| 1 + include/ui/console.h| 2 ++ ui/console.c| 9 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 14ad2b352d..102fa0b7e9 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -108,7 +108,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->guest_primary.surface.mem, MEMSLOT_GROUP_GUEST); if (!qxl->guest_primary.data) { -return; +goto end; } qxl_set_rect_to_surface(qxl, >dirty[0]); qxl->num_dirty_rects = 1; @@ -136,7 +136,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) } if (!qxl->guest_primary.data) { -return; +goto end; } for (i = 0; i < qxl->num_dirty_rects; i++) { if (qemu_spice_rect_is_empty(qxl->dirty+i)) { @@ -157,6 +157,11 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->dirty[i].bottom - qxl->dirty[i].top); } qxl->num_dirty_rects = 0; + +end: +if (qxl->render_update_cookie_num == 0) { +graphic_hw_update_done(qxl->ssd.dcl.con); +} } /* diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 98c7410032..188399acd1 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1178,6 +1178,7 @@ static const QXLInterface qxl_interface = { static const GraphicHwOps qxl_ops = { .gfx_update = qxl_hw_update, +.gfx_update_async = true, }; static void qxl_enter_vga_mode(PCIQXLDevice *d) diff --git a/include/ui/console.h b/include/ui/console.h index f981696848..281f9c145b 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -365,6 +365,7 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch) typedef struct GraphicHwOps { void (*invalidate)(void *opaque); void (*gfx_update)(void *opaque); +bool gfx_update_async; /* if true, calls graphic_hw_update_done() */ void (*text_update)(void *opaque, console_ch_t *text); void (*update_interval)(void *opaque, uint64_t interval); int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); @@ -380,6 +381,7 @@ void graphic_console_set_hwops(QemuConsole *con, void graphic_console_close(QemuConsole *con); void graphic_hw_update(QemuConsole *con); +void graphic_hw_update_done(QemuConsole *con); void graphic_hw_invalidate(QemuConsole *con); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); void graphic_hw_gl_block(QemuConsole *con, bool block); diff --git a/ui/console.c b/ui/console.c index 82d1ddac9c..3c941528d2 100644 --- a/ui/console.c +++ b/ui/console.c @@ -259,13 +259,22 @@ static void gui_setup_refresh(DisplayState *ds) ds->have_text = have_text; } +void graphic_hw_update_done(QemuConsole *con) +{ +} + void graphic_hw_update(QemuConsole *con) { +bool async = false; if (!con) { con = active_console; } if (con && con->hw_ops->gfx_update) { con->hw_ops->gfx_update(con->hw); +async = con->hw_ops->gfx_update_async; +} +if (!async) { +graphic_hw_update_done(con); } } -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 15/20] monitor: add qmp_return_get_monitor()
If necessary, add an helper that can be used to retrieve the associated monitor. This is useful for asynchronous commands that may have to update cur_mon for various reasons. Signed-off-by: Marc-André Lureau --- include/monitor/monitor.h | 3 +++ monitor/qmp.c | 11 +++ 2 files changed, 14 insertions(+) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index a81eeff5f8..6a2907a366 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -4,6 +4,7 @@ #include "block/block.h" #include "qapi/qapi-types-misc.h" #include "qemu/readline.h" +#include "qapi/qmp/dispatch.h" extern __thread Monitor *cur_mon; typedef struct MonitorHMP MonitorHMP; @@ -43,4 +44,6 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd); void monitor_fdset_dup_fd_remove(int dup_fd); int64_t monitor_fdset_dup_fd_find(int dup_fd); +Monitor *qmp_return_get_monitor(QmpReturn *qret); + #endif /* MONITOR_H */ diff --git a/monitor/qmp.c b/monitor/qmp.c index 056ad7b68b..df8b9d8d4f 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -390,3 +390,14 @@ void monitor_init_qmp(Chardev *chr, bool pretty) monitor_list_append(>common); } } + +Monitor *qmp_return_get_monitor(QmpReturn *qret) +{ +MonitorQMP *mon; + +if (!qret->session) { +return NULL; +} +mon = container_of(qret->session, MonitorQMP, session); +return >common; +} -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 13/20] scripts: learn 'async' qapi commands
Commands with the 'async' key will be registered as async type (see related commit), and will allow a synchronous (in scope callback) or asynchronous return (out-of-scope when ready, in idle etc) by keeping the given QmpReturn and calling qmp_return function later. Ex: { 'command': 'foo-async, 'data': {'arg': 'str'}, 'returns': 'Foo', 'async': true } generates the following marshaller: void qmp_marshal_foo_async(QDict *args, QmpReturn *qret) { Error *err = NULL; Visitor *v; q_obj_foo_async_arg arg = {0}; v = qmp_input_visitor_new(QOBJECT(args), true); visit_start_struct(v, NULL, NULL, 0, ); if (err) { goto out; } visit_type_q_obj_foo_async_arg_members(v, , ); if (!err) { visit_check_struct(v, ); } visit_end_struct(v, NULL); if (err) { goto out; } qmp_foo_async(arg.arg, qret); out: if (err) { qmp_return_error(qret, err); } visit_free(v); v = qapi_dealloc_visitor_new(); visit_start_struct(v, NULL, NULL, 0, NULL); visit_type_q_obj_foo_async_arg_members(v, , NULL); visit_end_struct(v, NULL); visit_free(v); } and a return helper: void qmp_foo_async_return(QmpReturn *qret, Foo *ret_in) { Error *err = NULL; QObject *ret_out = NULL; qmp_marshal_output_Foo(ret_in, _out, ); if (err) { qmp_return_error(qret, err); } else { qmp_return(qret, ret_out); } } The dispatched function may call the return helper within the calling scope or delay the return. To return an error, it can call qmp_return_error() directly instead. Signed-off-by: Marc-André Lureau --- scripts/qapi/commands.py| 151 scripts/qapi/common.py | 15 ++- scripts/qapi/doc.py | 3 +- scripts/qapi/introspect.py | 3 +- tests/qapi-schema/qapi-schema-test.json | 5 + tests/qapi-schema/qapi-schema-test.out | 8 ++ tests/qapi-schema/test-qapi.py | 8 +- tests/test-qmp-cmds.c | 60 ++ 8 files changed, 218 insertions(+), 35 deletions(-) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index b929e07be4..4c200c04ca 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -16,18 +16,36 @@ See the COPYING file in the top-level directory. from qapi.common import * -def gen_command_decl(name, arg_type, boxed, ret_type): -return mcgen(''' -%(c_type)s qmp_%(c_name)s(%(params)s); +def gen_command_decl(name, arg_type, boxed, ret_type, success_response, asyn): +if asyn: +extra = "QmpReturn *qret" +else: +extra = 'Error **errp' + +if asyn: +ret = mcgen(''' +void qmp_%(name)s(%(params)s); ''', - c_type=(ret_type and ret_type.c_type()) or 'void', - c_name=c_name(name), - params=build_params(arg_type, boxed, 'Error **errp')) + name=c_name(name), + params=build_params(arg_type, boxed, extra)) +if success_response: +ret += mcgen(''' +void qmp_%(name)s_return(QmpReturn *qret%(c_type)s); +''', +c_type=(", " + ret_type.c_type() if ret_type else ""), +name=c_name(name)) +return ret +else: +return mcgen(''' +%(c_type)s qmp_%(c_name)s(%(params)s); +''', + c_type=(ret_type and ret_type.c_type()) or 'void', + c_name=c_name(name), + params=build_params(arg_type, boxed, extra)) -def gen_call(name, arg_type, boxed, ret_type): -ret = '' +def gen_argstr(arg_type, boxed): argstr = '' if boxed: assert arg_type and not arg_type.is_empty() @@ -39,6 +57,13 @@ def gen_call(name, arg_type, boxed, ret_type): argstr += 'arg.has_%s, ' % c_name(memb.name) argstr += 'arg.%s, ' % c_name(memb.name) +return argstr + + +def gen_call(name, arg_type, boxed, ret_type): +ret = '' + +argstr = gen_argstr(arg_type, boxed) lhs = '' if ret_type: lhs = 'retval = ' @@ -60,6 +85,50 @@ def gen_call(name, arg_type, boxed, ret_type): return ret +def gen_async_call(name, arg_type, boxed): +argstr = gen_argstr(arg_type, boxed) + +push_indent() +ret = mcgen(''' + +qmp_%(c_name)s(%(args)sqret); +''', +c_name=c_name(name), args=argstr) + +pop_indent() +return ret + + +def gen_async_return(name, ret_type): +if ret_type: +return mcgen(''' + +void qmp_%(c_name)s_return(QmpReturn *qret, %(ret_type)s ret_in) +{ +Error *err = NULL; +QObject *ret_out = NULL; + +qmp_marshal_output_%(ret_c_name)s(ret_in, _out, ); + +if (err) { +qmp_return_error(qret, err); +} else { +qmp_return(qret, ret_out); +} +} +''', + c_name=c_name(name), + ret_type=ret_type.c_type(),
[Qemu-devel] [PATCH v5 14/20] qmp: add qmp_return_is_cancelled()
If the client is gone, and the session finished, no need to return. The async handler can use this information to avoid unnecessary work and exit earlier. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 8 qapi/qmp-dispatch.c | 10 ++ tests/test-qmp-cmds.c | 39 - 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 6aef0abc70..6673902e95 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -91,6 +91,14 @@ void qmp_return_free(QmpReturn *qret); void qmp_return(QmpReturn *qret, QObject *rsp); void qmp_return_error(QmpReturn *qret, Error *err); +/* + * @qmp_return_is_cancelled: + * + * Return true if the QmpReturn is cancelled, and free the QmpReturn + * in this case. + */ +bool qmp_return_is_cancelled(QmpReturn *qret); + void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); void qmp_register_async_command(QmpCommandList *cmds, const char *name, diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 1f493af67a..8653c17901 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -64,6 +64,16 @@ void qmp_return_free(QmpReturn *qret) } } +bool qmp_return_is_cancelled(QmpReturn *qret) +{ +if (!qret->session) { +qmp_return_free(qret); +return true; +} + +return false; +} + static void qmp_return_orderly(QmpReturn *qret) { QmpSession *session = qret->session; diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index f567ac2fb0..d4c27f0be1 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -34,17 +34,29 @@ void qmp_cmd_success_response(Error **errp) { } +static GMainLoop *loop; + static gboolean cmd_async_idle(gpointer user_data) { QmpReturn *qret = user_data; -qmp_cmd_async_return(qret, g_new0(Empty2, 1)); +if (!qret->session) { +g_assert(qmp_return_is_cancelled(qret)); +g_main_loop_quit(loop); +g_main_loop_unref(loop); +loop = NULL; +} else { +qmp_cmd_async_return(qret, g_new0(Empty2, 1)); +} return G_SOURCE_REMOVE; } void qmp_cmd_async(const char *filename, QmpReturn *qret) { +if (g_str_equal(filename, "cancel")) { +qmp_session_destroy(qret->session); +} g_idle_add(cmd_async_idle, qret); } @@ -425,6 +437,30 @@ static void test_qmp_return_async(void) qobject_unref(req); } +static void test_qmp_return_async_cancel(void) +{ +QmpReturnAsync a = { { 0, }, }; +QDict *args = qdict_new(); +QDict *req = qdict_new(); + +a.loop = g_main_loop_new(NULL, TRUE); +qmp_session_init(, _commands, + NULL, dispatch_return_async); + +qdict_put_str(args, "filename", "cancel"); +qdict_put_str(req, "execute", "cmd-async"); +qdict_put(req, "arguments", args); +qmp_dispatch(, QOBJECT(req), false); +g_assert(a.loop); + +loop = a.loop; +g_main_loop_run(loop); +g_assert(!loop); + +qmp_session_destroy(); +qobject_unref(req); +} + int main(int argc, char **argv) { g_test_init(, , NULL); @@ -439,6 +475,7 @@ int main(int argc, char **argv) g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial); g_test_add_func("/qmp/return_orderly", test_qmp_return_orderly); g_test_add_func("/qmp/return_async", test_qmp_return_async); +g_test_add_func("/qmp/return_async_cancel", test_qmp_return_async_cancel); test_qmp_init_marshal(_commands); g_test_run(); -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 11/20] QmpSession: return orderly
QEMU will gain support for asynchronous commands, and may thus finish commands in various order. However, the clients expect replies in order. Let's enforce ordering of replies in QmpReturn: starting from the older command, process each pending QmpReturn, and return until reaching one that is unfinished. Or if the command is OOB, it should return immediately. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 2 ++ qapi/qmp-dispatch.c | 61 ++--- tests/test-qmp-cmds.c | 33 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 7c9de9780d..92d6fd1afb 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -55,6 +55,8 @@ struct QmpSession { struct QmpReturn { QmpSession *session; QDict *rsp; +bool oob; +bool finished; QTAILQ_ENTRY(QmpReturn) entry; }; diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 4699a6715b..546a6c9f7b 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -25,6 +25,7 @@ QmpReturn *qmp_return_new(QmpSession *session, const QObject *request) const QDict *req = qobject_to(QDict, request); QObject *id = req ? qdict_get(req, "id") : NULL; +qret->oob = req ? qmp_is_oob(req) : false; qret->session = session; qret->rsp = qdict_new(); if (id) { @@ -39,6 +40,15 @@ QmpReturn *qmp_return_new(QmpSession *session, const QObject *request) return qret; } +static void qmp_return_free_with_lock(QmpReturn *qret) +{ +if (qret->session) { +QTAILQ_REMOVE(>session->pending, qret, entry); +} +qobject_unref(qret->rsp); +g_free(qret); +} + void qmp_return_free(QmpReturn *qret) { QmpSession *session = qret->session; @@ -46,21 +56,53 @@ void qmp_return_free(QmpReturn *qret) if (session) { qemu_mutex_lock(>pending_lock); } -QTAILQ_REMOVE(>pending, qret, entry); + +qmp_return_free_with_lock(qret); + if (session) { qemu_mutex_unlock(>pending_lock); } -qobject_unref(qret->rsp); -g_free(qret); +} + +static void qmp_return_orderly(QmpReturn *qret) +{ +QmpSession *session = qret->session; +QmpReturn *ret, *next; + +if (!session) { +/* the session was destroyed before return, discard */ +qmp_return_free(qret); +return; +} +if (qret->oob) { +session->return_cb(session, qret->rsp); +qmp_return_free(qret); +return; +} + +qret->finished = true; + +qemu_mutex_lock(>pending_lock); +/* + * Process the list of pending and call return_cb until reaching + * an unfinished. + */ +QTAILQ_FOREACH_SAFE(ret, >pending, entry, next) { +if (!ret->finished) { +break; +} +session->return_cb(session, ret->rsp); +ret->session = session; +qmp_return_free_with_lock(ret); +} + +qemu_mutex_unlock(>pending_lock); } void qmp_return(QmpReturn *qret, QObject *rsp) { qdict_put_obj(qret->rsp, "return", rsp ?: QOBJECT(qdict_new())); -if (qret->session) { -qret->session->return_cb(qret->session, qret->rsp); -} -qmp_return_free(qret); +qmp_return_orderly(qret); } void qmp_return_error(QmpReturn *qret, Error *err) @@ -70,10 +112,7 @@ void qmp_return_error(QmpReturn *qret, Error *err) qdict_put_str(qdict, "desc", error_get_pretty(err)); qdict_put_obj(qret->rsp, "error", QOBJECT(qdict)); error_free(err); -if (qret->session) { -qret->session->return_cb(qret->session, qret->rsp); -} -qmp_return_free(qret); +qmp_return_orderly(qret); } static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 8e46f88f6f..ece8726e96 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -333,6 +333,38 @@ static void test_dealloc_partial(void) qapi_free_UserDefTwo(ud2); } +typedef struct QmpReturnOrderly { +QmpSession session; +int returns; +} QmpReturnOrderly; + +static void dispatch_return_orderly(QmpSession *session, QDict *resp) +{ +QmpReturnOrderly *o = container_of(session, QmpReturnOrderly, session); + +o->returns++; +} + +static void test_qmp_return_orderly(void) +{ +QDict *dict = qdict_new(); +QmpReturnOrderly o = { { 0 }, }; +QmpReturn *r1, *r2, *r3; + +qmp_session_init(, _commands, NULL, dispatch_return_orderly); +r1 = qmp_return_new(, NULL); +qdict_put_str(dict, "exec-oob", "test"); +r2 = qmp_return_new(, QOBJECT(dict)); +r3 = qmp_return_new(, NULL); +qmp_return(r3, NULL); +g_assert_cmpint(o.returns, ==, 0); +qmp_return(r2, NULL); +g_assert_cmpint(o.returns, ==, 1); +qmp_return(r1, NULL); +g_assert_cmpint(o.returns, ==, 3); +qmp_session_destroy(); +qobject_unref(dict); +} int main(int argc, char
[Qemu-devel] [PATCH v5 12/20] qmp: introduce asynchronous command type
Add a new type of command, QmpCommandFuncAsync: those commands can return later thanks to QmpReturn. This commit introduces the new type and register function and teach qmp_dipatch() to call it without qmp_return(). The async_fn callback will be responsible for calling qmp_return(), either synchronously or asynchronously. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 10 +- qapi/qmp-dispatch.c | 27 --- qapi/qmp-registry.c | 27 --- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 92d6fd1afb..6aef0abc70 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -21,6 +21,7 @@ typedef struct QmpReturn QmpReturn; typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); +typedef void (QmpCommandAsyncFunc)(QDict *, QmpReturn *); typedef enum QmpCommandOptions { @@ -28,12 +29,16 @@ typedef enum QmpCommandOptions QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), QCO_ALLOW_PRECONFIG = (1U << 2), +QCO_ASYNC = (1U << 3), } QmpCommandOptions; typedef struct QmpCommand { const char *name; -QmpCommandFunc *fn; +union { +QmpCommandFunc *fn; +QmpCommandAsyncFunc *async_fn; +}; QmpCommandOptions options; QTAILQ_ENTRY(QmpCommand) node; bool enabled; @@ -88,6 +93,9 @@ void qmp_return_error(QmpReturn *qret, Error *err); void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); +void qmp_register_async_command(QmpCommandList *cmds, const char *name, +QmpCommandAsyncFunc *fn, +QmpCommandOptions options); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); void qmp_session_init(QmpSession *session, diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 546a6c9f7b..1f493af67a 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -171,7 +171,7 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, return dict; } -static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, +static QObject *do_qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob, Error **errp) { Error *local_err = NULL; @@ -193,7 +193,7 @@ static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, command = qdict_get_str(dict, "exec-oob"); oob = true; } -cmd = qmp_find_command(cmds, command); +cmd = qmp_find_command(session->cmds, command); if (cmd == NULL) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found", command); @@ -224,14 +224,19 @@ static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, qobject_ref(args); } -cmd->fn(args, , _err); -if (local_err) { -error_propagate(errp, local_err); -} else if (cmd->options & QCO_NO_SUCCESS_RESP) { -g_assert(!ret); -} else if (!ret) { -/* TODO turn into assertion */ -ret = QOBJECT(qdict_new()); + +if (cmd->options & QCO_ASYNC) { +cmd->async_fn(args, qmp_return_new(session, request)); +} else { +cmd->fn(args, , _err); +if (local_err) { +error_propagate(errp, local_err); +} else if (cmd->options & QCO_NO_SUCCESS_RESP) { +g_assert(!ret); +} else if (!ret) { +/* TODO turn into assertion */ +ret = QOBJECT(qdict_new()); +} } qobject_unref(args); @@ -304,7 +309,7 @@ void qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob) Error *err = NULL; QObject *ret; -ret = do_qmp_dispatch(session->cmds, request, allow_oob, ); +ret = do_qmp_dispatch(session, request, allow_oob, ); if (err) { qmp_return_error(qmp_return_new(session, request), err); } else if (ret) { diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index d0f9a1d3e3..0f3d521ce5 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -15,16 +15,37 @@ #include "qemu/osdep.h" #include "qapi/qmp/dispatch.h" -void qmp_register_command(QmpCommandList *cmds, const char *name, - QmpCommandFunc *fn, QmpCommandOptions options) + +static QmpCommand *qmp_command_new(QmpCommandList *cmds, const char *name, + QmpCommandOptions options) { QmpCommand *cmd = g_malloc0(sizeof(*cmd)); cmd->name = name; -cmd->fn = fn; cmd->enabled = true; cmd->options = options; QTAILQ_INSERT_TAIL(cmds, cmd, node); + +return cmd; +} + + +void qmp_register_command(QmpCommandList
[Qemu-devel] [PATCH v5 10/20] QmpSession: keep a queue of pending commands
The following commit will introduce asynchronous commands. Let's keep the session aware of the pending commands, so we can do interesting things like order the replies, or cancel pending operations when the client is gone. The queue needs a lock, since QmpReturn may be called from any thread. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 4 qapi/qmp-dispatch.c | 32 ++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 6c0d21968e..7c9de9780d 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -16,6 +16,7 @@ #include "qemu/queue.h" #include "qapi/qmp/json-parser.h" +#include "qemu/thread.h" typedef struct QmpReturn QmpReturn; @@ -47,11 +48,14 @@ struct QmpSession { const QmpCommandList *cmds; JSONMessageParser parser; QmpDispatchReturn *return_cb; +QemuMutex pending_lock; +QTAILQ_HEAD(, QmpReturn) pending; }; struct QmpReturn { QmpSession *session; QDict *rsp; +QTAILQ_ENTRY(QmpReturn) entry; }; /** diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 5f75dc27bd..4699a6715b 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -32,11 +32,24 @@ QmpReturn *qmp_return_new(QmpSession *session, const QObject *request) qdict_put_obj(qret->rsp, "id", id); } +qemu_mutex_lock(>pending_lock); +QTAILQ_INSERT_TAIL(>pending, qret, entry); +qemu_mutex_unlock(>pending_lock); + return qret; } void qmp_return_free(QmpReturn *qret) { +QmpSession *session = qret->session; + +if (session) { +qemu_mutex_lock(>pending_lock); +} +QTAILQ_REMOVE(>pending, qret, entry); +if (session) { +qemu_mutex_unlock(>pending_lock); +} qobject_unref(qret->rsp); g_free(qret); } @@ -44,7 +57,9 @@ void qmp_return_free(QmpReturn *qret) void qmp_return(QmpReturn *qret, QObject *rsp) { qdict_put_obj(qret->rsp, "return", rsp ?: QOBJECT(qdict_new())); -qret->session->return_cb(qret->session, qret->rsp); +if (qret->session) { +qret->session->return_cb(qret->session, qret->rsp); +} qmp_return_free(qret); } @@ -55,7 +70,9 @@ void qmp_return_error(QmpReturn *qret, Error *err) qdict_put_str(qdict, "desc", error_get_pretty(err)); qdict_put_obj(qret->rsp, "error", QOBJECT(qdict)); error_free(err); -qret->session->return_cb(qret->session, qret->rsp); +if (qret->session) { +qret->session->return_cb(qret->session, qret->rsp); +} qmp_return_free(qret); } @@ -219,17 +236,28 @@ void qmp_session_init(QmpSession *session, session, NULL); session->cmds = cmds; session->return_cb = return_cb; +qemu_mutex_init(>pending_lock); +QTAILQ_INIT(>pending); } void qmp_session_destroy(QmpSession *session) { +QmpReturn *ret, *next; + if (!session->return_cb) { return; } +qemu_mutex_lock(>pending_lock); +QTAILQ_FOREACH_SAFE(ret, >pending, entry, next) { +ret->session = NULL; +QTAILQ_REMOVE(>pending, ret, entry); +} +qemu_mutex_unlock(>pending_lock); session->cmds = NULL; session->return_cb = NULL; json_message_parser_destroy(>parser); +qemu_mutex_destroy(>pending_lock); } void qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob) -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 08/20] QmpSession: introduce QmpReturn
QmpReturn (and associated functions) is used during synchronous dispatch return for now. It helps to factor out some code for handling a reply context. In the following patches, the QmpReturn will be the basis upon which asynchronous reply will be handled: it will hold the context for a QMP command reply. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 34 - monitor/qmp.c | 6 +-- qapi/qmp-dispatch.c | 74 ++--- 3 files changed, 79 insertions(+), 35 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index b3ca6c9ff2..6c0d21968e 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -17,6 +17,8 @@ #include "qemu/queue.h" #include "qapi/qmp/json-parser.h" +typedef struct QmpReturn QmpReturn; + typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); typedef enum QmpCommandOptions @@ -47,6 +49,37 @@ struct QmpSession { QmpDispatchReturn *return_cb; }; +struct QmpReturn { +QmpSession *session; +QDict *rsp; +}; + +/** + * qmp_return_new: + * + * Allocates and initializes a QmpReturn. + */ +QmpReturn *qmp_return_new(QmpSession *session, const QObject *req); + +/** + * qmp_return_free: + * + * Free a QmpReturn. This shouldn't be needed if you actually return + * with qmp_return{_error}. + */ +void qmp_return_free(QmpReturn *qret); + +/** + * qmp_return{_error}: + * + * Construct the command reply, and call the + * return_cb() associated with the session. + * + * Finally, free the QmpReturn. + */ +void qmp_return(QmpReturn *qret, QObject *rsp); +void qmp_return_error(QmpReturn *qret, Error *err); + void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, @@ -67,7 +100,6 @@ void qmp_enable_command(QmpCommandList *cmds, const char *name); bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); -QDict *qmp_error_response(Error *err); void qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob); bool qmp_is_oob(const QDict *dict); diff --git a/monitor/qmp.c b/monitor/qmp.c index cd29494e28..056ad7b68b 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -179,7 +179,6 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) void monitor_qmp_bh_dispatcher(void *data) { QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock(); -QDict *rsp; bool need_resume; MonitorQMP *mon; @@ -198,11 +197,10 @@ void monitor_qmp_bh_dispatcher(void *data) trace_monitor_qmp_cmd_in_band(qobject_get_try_str(id) ?: ""); monitor_qmp_dispatch(mon, req_obj->req); } else { +QmpSession *session = _obj->mon->session; assert(req_obj->err); -rsp = qmp_error_response(req_obj->err); +qmp_return_error(qmp_return_new(session, req_obj->req), req_obj->err); req_obj->err = NULL; -qmp_send_response(req_obj->mon, rsp); -qobject_unref(rsp); } if (need_resume) { diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index f2c376d005..405cb291b1 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -19,6 +19,46 @@ #include "qapi/qmp/qbool.h" #include "sysemu/sysemu.h" +QmpReturn *qmp_return_new(QmpSession *session, const QObject *request) +{ +QmpReturn *qret = g_new0(QmpReturn, 1); +const QDict *req = qobject_to(QDict, request); +QObject *id = req ? qdict_get(req, "id") : NULL; + +qret->session = session; +qret->rsp = qdict_new(); +if (id) { +qobject_ref(id); +qdict_put_obj(qret->rsp, "id", id); +} + +return qret; +} + +void qmp_return_free(QmpReturn *qret) +{ +qobject_unref(qret->rsp); +g_free(qret); +} + +void qmp_return(QmpReturn *qret, QObject *rsp) +{ +qdict_put_obj(qret->rsp, "return", rsp ?: QOBJECT(qdict_new())); +qret->session->return_cb(qret->session, qret->rsp); +qmp_return_free(qret); +} + +void qmp_return_error(QmpReturn *qret, Error *err) +{ +qdict_put_obj(qret->rsp, "error", + qobject_from_jsonf_nofail("{ 'class': %s, 'desc': %s }", + QapiErrorClass_str(error_get_class(err)), + error_get_pretty(err))); +error_free(err); +qret->session->return_cb(qret->session, qret->rsp); +qmp_return_free(qret); +} + static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, Error **errp) { @@ -143,17 +183,6 @@ static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, return ret; } -QDict *qmp_error_response(Error *err) -{ -QDict *rsp; - -rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", -
[Qemu-devel] [PATCH v5 09/20] qmp: simplify qmp_return_error()
It's simple, probably more efficient, to hand-craft the dict. Signed-off-by: Marc-André Lureau --- qapi/qmp-dispatch.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 405cb291b1..5f75dc27bd 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -50,10 +50,10 @@ void qmp_return(QmpReturn *qret, QObject *rsp) void qmp_return_error(QmpReturn *qret, Error *err) { -qdict_put_obj(qret->rsp, "error", - qobject_from_jsonf_nofail("{ 'class': %s, 'desc': %s }", - QapiErrorClass_str(error_get_class(err)), - error_get_pretty(err))); +QDict *qdict = qdict_new(); +qdict_put_str(qdict, "class", QapiErrorClass_str(error_get_class(err))); +qdict_put_str(qdict, "desc", error_get_pretty(err)); +qdict_put_obj(qret->rsp, "error", QOBJECT(qdict)); error_free(err); qret->session->return_cb(qret->session, qret->rsp); qmp_return_free(qret); -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 07/20] qga: simplify dispatch_return_cb
Fold send_response(). qobject_to_json() can't return NULL (it will crash if allocation failed, either in memcpy() or abort from g_realloc()). Signed-off-by: Marc-André Lureau --- qga/main.c | 19 +++ 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/qga/main.c b/qga/main.c index b005550c70..66fe7ac3de 100644 --- a/qga/main.c +++ b/qga/main.c @@ -522,8 +522,9 @@ fail: #endif } -static int send_response(GAState *s, const QDict *rsp) +static void dispatch_return_cb(QmpSession *session, QDict *rsp) { +GAState *s = container_of(session, GAState, session); const char *buf; QString *payload_qstr, *response_qstr; GIOStatus status; @@ -531,9 +532,6 @@ static int send_response(GAState *s, const QDict *rsp) g_assert(rsp && s->channel); payload_qstr = qobject_to_json(QOBJECT(rsp)); -if (!payload_qstr) { -return -EINVAL; -} if (s->delimit_response) { s->delimit_response = false; @@ -550,18 +548,7 @@ static int send_response(GAState *s, const QDict *rsp) status = ga_channel_write_all(s->channel, buf, strlen(buf)); qobject_unref(response_qstr); if (status != G_IO_STATUS_NORMAL) { -return -EIO; -} - -return 0; -} - -static void dispatch_return_cb(QmpSession *session, QDict *rsp) -{ -GAState *s = container_of(session, GAState, session); -int ret = send_response(s, rsp); -if (ret < 0) { -g_warning("error sending response: %s", strerror(-ret)); +g_warning("Failed sending response"); } } -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 06/20] monitor: use qmp session to parse json feed
Use the QmpSession json parser introduced in previous patch to generalize the handling in both qemu & qemu-ga. Unfortunately, since the introduction of OOB, it's not as common as it was before that. We may want to move some of OOB logic in common qmp-dispatch.c/QmpSession though. The QEMU monitor has peculiar handling of the stream of commands, for OOB command processing, which can be solved by overriding the json emit callback. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h| 1 + include/qapi/qmp/json-parser.h | 7 --- monitor/monitor-internal.h | 1 - monitor/qmp.c | 13 + qapi/qmp-dispatch.c| 4 +++- qga/main.c | 2 +- qobject/json-streamer.c| 3 +-- tests/test-qmp-cmds.c | 11 ++- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index c84edff7d2..b3ca6c9ff2 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -53,6 +53,7 @@ const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); void qmp_session_init(QmpSession *session, const QmpCommandList *cmds, + JSONMessageEmit *emit, QmpDispatchReturn *return_cb); static inline void qmp_session_feed(QmpSession *session, const char *buf, size_t count) diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h index 7345a9bd5c..6f168e8007 100644 --- a/include/qapi/qmp/json-parser.h +++ b/include/qapi/qmp/json-parser.h @@ -14,6 +14,8 @@ #ifndef QAPI_QMP_JSON_PARSER_H #define QAPI_QMP_JSON_PARSER_H +typedef void (JSONMessageEmit)(void *opaque, QObject *json, Error *err); + typedef struct JSONLexer { int start_state, state; GString *token; @@ -21,7 +23,7 @@ typedef struct JSONLexer { } JSONLexer; typedef struct JSONMessageParser { -void (*emit)(void *opaque, QObject *json, Error *err); +JSONMessageEmit *emit; void *opaque; va_list *ap; JSONLexer lexer; @@ -32,8 +34,7 @@ typedef struct JSONMessageParser { } JSONMessageParser; void json_message_parser_init(JSONMessageParser *parser, - void (*emit)(void *opaque, QObject *json, - Error *err), + JSONMessageEmit *emit, void *opaque, va_list *ap); void json_message_parser_feed(JSONMessageParser *parser, diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 65d587eafb..65cf668b20 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -125,7 +125,6 @@ struct MonitorHMP { typedef struct { Monitor common; -JSONMessageParser parser; bool pretty; /* * When a client connects, we're in capabilities negotiation mode. diff --git a/monitor/qmp.c b/monitor/qmp.c index b215cb70f3..cd29494e28 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -217,7 +217,7 @@ void monitor_qmp_bh_dispatcher(void *data) static void handle_qmp_command(void *opaque, QObject *req, Error *err) { -MonitorQMP *mon = opaque; +MonitorQMP *mon = container_of(opaque, MonitorQMP, session); QObject *id = NULL; QDict *qdict; QMPRequest *req_obj; @@ -279,7 +279,7 @@ static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) { MonitorQMP *mon = opaque; -json_message_parser_feed(>parser, (const char *) buf, size); +qmp_session_feed(>session, (const char *) buf, size); } static QDict *qmp_greeting(MonitorQMP *mon) @@ -309,7 +309,9 @@ static void monitor_qmp_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: qmp_session_init(>session, - _cap_negotiation_commands, dispatch_return_cb); + _cap_negotiation_commands, + handle_qmp_command, + dispatch_return_cb); monitor_qmp_caps_reset(mon); data = qmp_greeting(mon); qmp_send_response(mon, data); @@ -325,9 +327,6 @@ static void monitor_qmp_event(void *opaque, int event) */ monitor_qmp_cleanup_queues(mon); qmp_session_destroy(>session); -json_message_parser_destroy(>parser); -json_message_parser_init(>parser, handle_qmp_command, - mon, NULL); mon_refcount--; monitor_fdsets_cleanup(); break; @@ -337,7 +336,6 @@ static void monitor_qmp_event(void *opaque, int event) void monitor_data_destroy_qmp(MonitorQMP *mon) { qmp_session_destroy(>session); -json_message_parser_destroy(>parser); qemu_mutex_destroy(>qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); g_queue_free(mon->qmp_requests); @@ -373,7 +371,6 @@ void monitor_init_qmp(Chardev *chr, bool pretty)
[Qemu-devel] [PATCH v5 05/20] QmpSession: add json parser and use it in qga
Move JSON parser to QmpSession, and implement a simple handler to check the parsed tokens and call qmp_dispatch(). This is enough for a simple QMP client, like QGA. The QEMU monitor has more complicated handling of dispatching which will be addressed in a following patch to benefit from more common code. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 7 +++ qapi/qmp-dispatch.c | 19 +++ qga/main.c | 31 +-- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index d1ce631a93..c84edff7d2 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -15,6 +15,7 @@ #define QAPI_QMP_DISPATCH_H #include "qemu/queue.h" +#include "qapi/qmp/json-parser.h" typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); @@ -42,6 +43,7 @@ typedef void (QmpDispatchReturn) (QmpSession *session, QDict *rsp); struct QmpSession { const QmpCommandList *cmds; +JSONMessageParser parser; QmpDispatchReturn *return_cb; }; @@ -52,6 +54,11 @@ const QmpCommand *qmp_find_command(const QmpCommandList *cmds, void qmp_session_init(QmpSession *session, const QmpCommandList *cmds, QmpDispatchReturn *return_cb); +static inline void +qmp_session_feed(QmpSession *session, const char *buf, size_t count) +{ +json_message_parser_feed(>parser, buf, count); +} void qmp_session_destroy(QmpSession *session); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 37b058cf97..803ec626cd 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -163,6 +163,23 @@ bool qmp_is_oob(const QDict *dict) && !qdict_haskey(dict, "execute"); } +static void qmp_json_emit(void *opaque, QObject *obj, Error *err) +{ +QmpSession *session = opaque; + +assert(!obj != !err); + +if (err) { +QDict *rsp = qmp_error_response(err); +session->return_cb(session, rsp); +qobject_unref(rsp); +} else { +qmp_dispatch(session, obj, false); +} + +qobject_unref(obj); +} + void qmp_session_init(QmpSession *session, const QmpCommandList *cmds, QmpDispatchReturn *return_cb) @@ -170,6 +187,7 @@ void qmp_session_init(QmpSession *session, assert(return_cb); assert(!session->return_cb); +json_message_parser_init(>parser, qmp_json_emit, session, NULL); session->cmds = cmds; session->return_cb = return_cb; } @@ -182,6 +200,7 @@ void qmp_session_destroy(QmpSession *session) session->cmds = NULL; session->return_cb = NULL; +json_message_parser_destroy(>parser); } void qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob) diff --git a/qga/main.c b/qga/main.c index c291d06491..057368eb16 100644 --- a/qga/main.c +++ b/qga/main.c @@ -19,7 +19,6 @@ #include #endif #include "qemu-common.h" -#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qstring.h" @@ -75,7 +74,6 @@ typedef struct GAConfig GAConfig; struct GAState { QmpSession session; -JSONMessageParser parser; GMainLoop *main_loop; GAChannel *channel; bool virtio; /* fastpath to check for virtio to deal with poll() quirks */ @@ -567,31 +565,6 @@ static void dispatch_return_cb(QmpSession *session, QDict *rsp) } } -/* handle requests/control events coming in over the channel */ -static void process_event(void *opaque, QObject *obj, Error *err) -{ -GAState *s = opaque; -int ret; - -g_debug("process_event: called"); -assert(!obj != !err); - -if (err) { -QDict *rsp = qmp_error_response(err); - -ret = send_response(s, rsp); -if (ret < 0) { -g_warning("error sending error response: %s", strerror(-ret)); -} -qobject_unref(rsp); -} else { -g_debug("processing command"); -qmp_dispatch(>session, obj, false); -} - -qobject_unref(obj); -} - /* false return signals GAChannel to close the current client connection */ static gboolean channel_event_cb(GIOCondition condition, gpointer data) { @@ -607,7 +580,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) case G_IO_STATUS_NORMAL: buf[count] = 0; g_debug("read data, count: %d, data: %s", (int)count, buf); -json_message_parser_feed(>parser, (char *)buf, (int)count); +qmp_session_feed(>session, (char *)buf, (int)count); break; case G_IO_STATUS_EOF: g_debug("received EOF"); @@ -1346,7 +1319,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) s->command_state = ga_command_state_new(); ga_command_state_init(s,
[Qemu-devel] [PATCH v5 04/20] QmpSession: add a return callback
Introduce a return_cb to allow delaying finishing the dispatch and sending the response asynchronously. For now, this is just modifying qmp_dispatch() to call the callback synchronously. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 10 -- monitor/qmp.c | 47 ++--- qapi/qmp-dispatch.c | 22 +--- qga/main.c | 34 +++--- tests/test-qmp-cmds.c | 69 ++--- 5 files changed, 98 insertions(+), 84 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 3b53cfd788..d1ce631a93 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -38,16 +38,20 @@ typedef struct QmpCommand typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; typedef struct QmpSession QmpSession; +typedef void (QmpDispatchReturn) (QmpSession *session, QDict *rsp); struct QmpSession { const QmpCommandList *cmds; +QmpDispatchReturn *return_cb; }; void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); -void qmp_session_init(QmpSession *session, const QmpCommandList *cmds); +void qmp_session_init(QmpSession *session, + const QmpCommandList *cmds, + QmpDispatchReturn *return_cb); void qmp_session_destroy(QmpSession *session); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -56,8 +60,8 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); -QDict *qmp_dispatch(QmpSession *session, QObject *request, -bool allow_oob); +void qmp_dispatch(QmpSession *session, QObject *request, + bool allow_oob); bool qmp_is_oob(const QDict *dict); typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); diff --git a/monitor/qmp.c b/monitor/qmp.c index dd72a0d8cf..b215cb70f3 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -96,45 +96,35 @@ void qmp_send_response(MonitorQMP *mon, const QDict *rsp) qobject_unref(json); } -/* - * Emit QMP response @rsp with ID @id to @mon. - * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP. - * Nothing is emitted then. - */ -static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp) +static void dispatch_return_cb(QmpSession *session, QDict *rsp) { -if (rsp) { -qmp_send_response(mon, rsp); +MonitorQMP *mon = container_of(session, MonitorQMP, session); + +if (mon->session.cmds == _cap_negotiation_commands) { +QDict *error = qdict_get_qdict(rsp, "error"); +if (error +&& !g_strcmp0(qdict_get_try_str(error, "class"), + QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) { +/* Provide a more useful error message */ +qdict_del(error, "desc"); +qdict_put_str(error, "desc", "Expecting capabilities negotiation" + " with 'qmp_capabilities'"); +} } + +qmp_send_response(mon, rsp); } static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req) { Monitor *old_mon; -QDict *rsp; -QDict *error; old_mon = cur_mon; cur_mon = >common; -rsp = qmp_dispatch(>session, req, qmp_oob_enabled(mon)); +qmp_dispatch(>session, req, qmp_oob_enabled(mon)); cur_mon = old_mon; - -if (mon->session.cmds == _cap_negotiation_commands) { -error = qdict_get_qdict(rsp, "error"); -if (error -&& !g_strcmp0(qdict_get_try_str(error, "class"), -QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) { -/* Provide a more useful error message */ -qdict_del(error, "desc"); -qdict_put_str(error, "desc", "Expecting capabilities negotiation" - " with 'qmp_capabilities'"); -} -} - -monitor_qmp_respond(mon, rsp); -qobject_unref(rsp); } /* @@ -211,7 +201,7 @@ void monitor_qmp_bh_dispatcher(void *data) assert(req_obj->err); rsp = qmp_error_response(req_obj->err); req_obj->err = NULL; -monitor_qmp_respond(mon, rsp); +qmp_send_response(req_obj->mon, rsp); qobject_unref(rsp); } @@ -318,7 +308,8 @@ static void monitor_qmp_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: -qmp_session_init(>session, _cap_negotiation_commands); +qmp_session_init(>session, + _cap_negotiation_commands, dispatch_return_cb); monitor_qmp_caps_reset(mon); data =
[Qemu-devel] [PATCH v5 03/20] qmp: add QmpSession
This structure will hold various data related to a QMP client session: the list of commands, the parser, the callbacks, the pending operations... Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 10 +- monitor/misc.c | 6 +++--- monitor/monitor-internal.h | 2 +- monitor/monitor.c | 2 +- monitor/qmp.c | 8 +--- qapi/qmp-dispatch.c | 15 --- qga/main.c | 5 - tests/test-qmp-cmds.c | 28 ++-- 8 files changed, 57 insertions(+), 19 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 5a9cf82472..3b53cfd788 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -37,10 +37,18 @@ typedef struct QmpCommand typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; +typedef struct QmpSession QmpSession; + +struct QmpSession { +const QmpCommandList *cmds; +}; + void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); +void qmp_session_init(QmpSession *session, const QmpCommandList *cmds); +void qmp_session_destroy(QmpSession *session); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -48,7 +56,7 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); -QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, +QDict *qmp_dispatch(QmpSession *session, QObject *request, bool allow_oob); bool qmp_is_oob(const QDict *dict); diff --git a/monitor/misc.c b/monitor/misc.c index a0fc5111c5..a23c1b8ba4 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -253,7 +253,7 @@ CommandInfoList *qmp_query_commands(Error **errp) assert(monitor_is_qmp(cur_mon)); mon = container_of(cur_mon, MonitorQMP, common); -qmp_for_each_command(mon->commands, query_commands_cb, ); +qmp_for_each_command(mon->session.cmds, query_commands_cb, ); return list; } @@ -363,7 +363,7 @@ void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, assert(monitor_is_qmp(cur_mon)); mon = container_of(cur_mon, MonitorQMP, common); -if (mon->commands == _commands) { +if (mon->session.cmds == _commands) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "Capabilities negotiation is already complete, command " "ignored"); @@ -374,7 +374,7 @@ void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, return; } -mon->commands = _commands; +mon->session.cmds = _commands; } /* Set the current CPU defined by the user. Callers must hold BQL. */ diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index b0a028dbf8..65d587eafb 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -133,7 +133,7 @@ typedef struct { * qmp_capabilities succeeds, we go into command mode, and * @command becomes _commands. */ -const QmpCommandList *commands; +QmpSession session; bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* diff --git a/monitor/monitor.c b/monitor/monitor.c index 3ef28171c0..9d918c9952 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -262,7 +262,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) } qmp_mon = container_of(mon, MonitorQMP, common); -if (qmp_mon->commands != _cap_negotiation_commands) { +if (qmp_mon->session.cmds != _cap_negotiation_commands) { qmp_send_response(qmp_mon, qdict); } } diff --git a/monitor/qmp.c b/monitor/qmp.c index e1b196217d..dd72a0d8cf 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -117,11 +117,11 @@ static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req) old_mon = cur_mon; cur_mon = >common; -rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon)); +rsp = qmp_dispatch(>session, req, qmp_oob_enabled(mon)); cur_mon = old_mon; -if (mon->commands == _cap_negotiation_commands) { +if (mon->session.cmds == _cap_negotiation_commands) { error = qdict_get_qdict(rsp, "error"); if (error && !g_strcmp0(qdict_get_try_str(error, "class"), @@ -318,7 +318,7 @@ static void monitor_qmp_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: -mon->commands = _cap_negotiation_commands; +qmp_session_init(>session, _cap_negotiation_commands);
[Qemu-devel] [PATCH v5 02/20] json-lexer: make it safe to call destroy multiple times
We can easily avoid the burden of checking if the lexer was initialized prior to calling destroy by the caller, let's do it. This allows simplification in state tracking with the following patch, "qmp: add QmpSession" can call qmp_session_destroy() multiple times, which in turns calls json_lexer_destroy(). Signed-off-by: Marc-André Lureau --- qobject/json-lexer.c | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 632320d72d..fa7a2c43a8 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -361,5 +361,8 @@ void json_lexer_flush(JSONLexer *lexer) void json_lexer_destroy(JSONLexer *lexer) { -g_string_free(lexer->token, true); +if (lexer->token) { +g_string_free(lexer->token, true); +lexer->token = NULL; +} } -- 2.22.0.428.g6d5b264208
[Qemu-devel] [PATCH v5 01/20] qmp: constify QmpCommand and list
Since 0b69f6f72ce47a37a749b056b6d5ec64c61f11e8 "qapi: remove qmp_unregister_command()", the command list can be declared const. Signed-off-by: Marc-André Lureau --- include/qapi/qmp/dispatch.h | 9 + monitor/misc.c | 2 +- monitor/monitor-internal.h | 2 +- qapi/qmp-dispatch.c | 6 +++--- qapi/qmp-registry.c | 6 +++--- qga/commands.c | 2 +- qga/main.c | 6 +++--- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 9aa426a398..5a9cf82472 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -39,7 +39,8 @@ typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name); +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, + const char *name); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -47,13 +48,13 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, bool allow_oob); bool qmp_is_oob(const QDict *dict); -typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); +typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque); #endif diff --git a/monitor/misc.c b/monitor/misc.c index 00338c002a..a0fc5111c5 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -230,7 +230,7 @@ static void hmp_info_help(Monitor *mon, const QDict *qdict) help_cmd(mon, "info"); } -static void query_commands_cb(QmpCommand *cmd, void *opaque) +static void query_commands_cb(const QmpCommand *cmd, void *opaque) { CommandInfoList *info, **list = opaque; diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 7760b22ba3..b0a028dbf8 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -133,7 +133,7 @@ typedef struct { * qmp_capabilities succeeds, we go into command mode, and * @command becomes _commands. */ -QmpCommandList *commands; +const QmpCommandList *commands; bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index e2c366e09e..f9d43046aa 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -75,14 +75,14 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, return dict; } -static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, +static QObject *do_qmp_dispatch(const QmpCommandList *cmds, QObject *request, bool allow_oob, Error **errp) { Error *local_err = NULL; bool oob; const char *command; QDict *args, *dict; -QmpCommand *cmd; +const QmpCommand *cmd; QObject *ret = NULL; dict = qmp_dispatch_check_obj(request, allow_oob, errp); @@ -163,7 +163,7 @@ bool qmp_is_oob(const QDict *dict) && !qdict_haskey(dict, "execute"); } -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, bool allow_oob) { Error *err = NULL; diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index ca00f74795..d0f9a1d3e3 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -27,7 +27,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QTAILQ_INSERT_TAIL(cmds, cmd, node); } -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name) +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name) { QmpCommand *cmd; @@ -77,10 +77,10 @@ bool qmp_has_success_response(const QmpCommand *cmd) return !(cmd->options & QCO_NO_SUCCESS_RESP); } -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque) { -QmpCommand *cmd; +const QmpCommand *cmd; QTAILQ_FOREACH(cmd, cmds, node) { fn(cmd, opaque); diff --git a/qga/commands.c b/qga/commands.c index 0c7d1385c2..05e9ab6c3d 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -54,7 +54,7
[Qemu-devel] [PATCH v5 00/20] monitor: add asynchronous command type
Hi, HMP and QMP commands are handled synchronously in qemu today. But there are benefits allowing the command handler to re-enter the main loop if the command cannot be handled synchronously, or if it is long-lasting. Some bugs such as rhbz#1230527 are difficult to solve without it. The common solution is to use a pair of command+event in this case. But this approach has a number of issues: - you can't "fix" an existing command: you need a new API, and ad-hoc documentation for that command+signal association, and old/broken command deprecation - since the reply event is broadcasted and 'id' is used for matching the request, it may conflict with other clients request 'id' space - it is arguably less efficient and elegant (weird API, useless return in most cases, broadcast reply, no cancelling on disconnect etc) The following series implements an async command solution instead. By introducing a session context and a command return handler, it can: - defer the return, allowing the mainloop to reenter - return only to the caller (instead of broadcast events for reply) - optionnally allow cancellation when the client is gone - track on-going qapi command(s) per client/session and without introduction of new QMP APIs or client visible change. Existing qemu commands can be gradually replaced by async:true variants when needed, while carefully reviewing the concurrency aspects. The async:true commands marshaller helpers are splitted in half, the calling and return functions. The command is called with a QmpReturn context, that can return immediately or later, using the generated return helper, which allows for a step-by-step conversion. The screendump command is converted to an async:true version to solve rhbz#1230527. The command shows basic cancellation (this could be extended if needed). It could be further improved to do asynchronous IO writes as well. v5: - rebased v4: - rebased, mostly adapting to new OOB code (there was not much feedback in v3 for the async command part, but preliminary patches got merged!) - drop the RFC status v3: - complete rework, dropping the asynchronous commands visibility from the protocol side entirely (until there is a real need for it) - rebased, with a few preliminary cleanup patches - teach asynchronous commands to HMP v2: - documentation fixes and improvements - fix calling async commands sync without id - fix bad hmp monitor assert - add a few extra asserts - add async with no-id failure and screendump test Marc-André Lureau (20): qmp: constify QmpCommand and list json-lexer: make it safe to call destroy multiple times qmp: add QmpSession QmpSession: add a return callback QmpSession: add json parser and use it in qga monitor: use qmp session to parse json feed qga: simplify dispatch_return_cb QmpSession: introduce QmpReturn qmp: simplify qmp_return_error() QmpSession: keep a queue of pending commands QmpSession: return orderly qmp: introduce asynchronous command type scripts: learn 'async' qapi commands qmp: add qmp_return_is_cancelled() monitor: add qmp_return_get_monitor() console: add graphic_hw_update_done() console: make screendump asynchronous monitor: start making qmp_human_monitor_command() asynchronous monitor: teach HMP about asynchronous commands hmp: call the asynchronous QMP screendump to fix outdated/glitches hmp-commands.hx | 3 +- hw/display/qxl-render.c | 9 +- hw/display/qxl.c| 1 + include/monitor/monitor.h | 3 + include/qapi/qmp/dispatch.h | 89 +- include/qapi/qmp/json-parser.h | 7 +- include/ui/console.h| 4 + monitor/hmp-cmds.c | 6 +- monitor/hmp.c | 110 +++- monitor/misc.c | 46 + monitor/monitor-internal.h | 12 +- monitor/monitor.c | 2 +- monitor/qmp.c | 79 - qapi/misc.json | 3 +- qapi/qmp-dispatch.c | 214 +++- qapi/qmp-registry.c | 33 +++- qapi/ui.json| 3 +- qga/commands.c | 2 +- qga/main.c | 51 ++ qobject/json-lexer.c| 5 +- qobject/json-streamer.c | 3 +- scripts/qapi/commands.py| 151 ++--- scripts/qapi/common.py | 15 +- scripts/qapi/doc.py | 3 +- scripts/qapi/introspect.py | 3 +- tests/qapi-schema/qapi-schema-test.json | 5 + tests/qapi-schema/qapi-schema-test.out | 8 + tests/qapi-schema/test-qapi.py | 8 +- tests/test-qmp-cmds.c | 206 +++ ui/console.c| 100 +-- 30 files changed,
Re: [Qemu-devel] [PATCH v4 17/20] console: make screendump asynchronous
On Wed, Apr 10, 2019 at 12:49 PM Gerd Hoffmann wrote: > > > +static void qmp_screendump_finish(QemuConsole *con, struct qmp_screendump > > *dump) > > +{ > > +Error *err = NULL; > > +DisplaySurface *surface; > > +Monitor *prev_mon = cur_mon; > > Why this is needed? > ppm_save() calls qemu_open() which may lookup fd associated to the monitor: monitor_fdset_get_fd(). Interestingly, it seems fdset are not coupled with the current monitor, so it's probably unnecessary to update the monitor to the one associated with the command invocation. > > +/* > > + * FIXME: async save with coroutine? it would have to copy or > > + * lock the surface. > > + */ > > +ppm_save(dump->filename, surface, ); > > DisplaySurface is just a thin layer above pixman images these days. > Pixman images are reference counted, so you can > pixman_image_ref(surface->image) to make sure it doesn't disappear > underneath you, then pass the pixman image to ppm_save. ppm_save() is still synchronous. I suppose you suggested that for a future async version. (note that in this case, ref the surface is probably not sufficient, as it could be mutated while it is being saved) -- Marc-André Lureau